From b9c609e41381b809a97e3397336c246aa8748a32 Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Sat, 10 May 2025 22:57:13 +0800 Subject: [PATCH] Added upgrader for forward-auth - Added v3.2.1 to v3.2.2 upgrader for new forward auth logic - Optimized css in sso html page --- src/mod/update/updatelogic.go | 7 ++ src/mod/update/v322/typedef321.go | 141 ++++++++++++++++++++++ src/mod/update/v322/typedef322.go | 93 +++++++++++++++ src/mod/update/v322/v322.go | 191 ++++++++++++++++++++++++++++++ src/web/components/sso.html | 56 +++++---- 5 files changed, 463 insertions(+), 25 deletions(-) create mode 100644 src/mod/update/v322/typedef321.go create mode 100644 src/mod/update/v322/typedef322.go create mode 100644 src/mod/update/v322/v322.go diff --git a/src/mod/update/updatelogic.go b/src/mod/update/updatelogic.go index 8577e7b..ccb7b24 100644 --- a/src/mod/update/updatelogic.go +++ b/src/mod/update/updatelogic.go @@ -3,6 +3,7 @@ package update import ( v308 "imuslab.com/zoraxy/mod/update/v308" v315 "imuslab.com/zoraxy/mod/update/v315" + v322 "imuslab.com/zoraxy/mod/update/v322" ) // Updater Core logic @@ -19,6 +20,12 @@ func runUpdateRoutineWithVersion(fromVersion int, toVersion int) { if err != nil { panic(err) } + } else if fromVersion == 321 && toVersion == 322 { + //Updating from v3.2.1 to v3.2.2 + err := v322.UpdateFrom321To322() + if err != nil { + panic(err) + } } //ADD MORE VERSIONS HERE diff --git a/src/mod/update/v322/typedef321.go b/src/mod/update/v322/typedef321.go new file mode 100644 index 0000000..7d3e72d --- /dev/null +++ b/src/mod/update/v322/typedef321.go @@ -0,0 +1,141 @@ +package v322 + +import ( + "net/http" + "sync" + + "imuslab.com/zoraxy/mod/dynamicproxy/loadbalance" + "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy" + "imuslab.com/zoraxy/mod/dynamicproxy/rewrite" +) + +type ProxyType int + +// Pull from ratelimit.go +type RequestCountPerIpTable struct { + table sync.Map +} + +// Pull from special.go +type RoutingRule struct { + ID string //ID of the routing rule + Enabled bool //If the routing rule enabled + UseSystemAccessControl bool //Pass access control check to system white/black list, set this to false to bypass white/black list + MatchRule func(r *http.Request) bool + RoutingHandler func(http.ResponseWriter, *http.Request) +} + +const ( + ProxyTypeRoot ProxyType = iota //Root Proxy, everything not matching will be routed here + ProxyTypeHost //Host Proxy, match by host (domain) name + ProxyTypeVdir //Virtual Directory Proxy, match by path prefix +) + +/* Basic Auth Related Data structure*/ +// Auth credential for basic auth on certain endpoints +type BasicAuthCredentials struct { + Username string + PasswordHash string +} + +// Auth credential for basic auth on certain endpoints +type BasicAuthUnhashedCredentials struct { + Username string + Password string +} + +// Paths to exclude in basic auth enabled proxy handler +type BasicAuthExceptionRule struct { + PathPrefix string +} + +/* Routing Rule Data Structures */ + +// A Virtual Directory endpoint, provide a subset of ProxyEndpoint for better +// program structure than directly using ProxyEndpoint +type VirtualDirectoryEndpoint struct { + MatchingPath string //Matching prefix of the request path, also act as key + Domain string //Domain or IP to proxy to + RequireTLS bool //Target domain require TLS + SkipCertValidations bool //Set to true to accept self signed certs + Disabled bool //If the rule is enabled +} + +// Rules and settings for header rewriting +type HeaderRewriteRules struct { + UserDefinedHeaders []*rewrite.UserDefinedHeader //Custom headers to append when proxying requests from this endpoint + RequestHostOverwrite string //If not empty, this domain will be used to overwrite the Host field in request header + HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers + EnablePermissionPolicyHeader bool //Enable injection of permission policy header + PermissionPolicy *permissionpolicy.PermissionsPolicy //Permission policy header + DisableHopByHopHeaderRemoval bool //Do not remove hop-by-hop headers + +} + +/* + + Authentication Providers + +*/ + +const ( + AuthMethodNone AuthMethod = iota //No authentication required + AuthMethodBasic //Basic Auth + AuthMethodAuthelia //Authelia + AuthMethodOauth2 //Oauth2 + AuthMethodAuthentik +) + +type AuthenticationProvider struct { + AuthMethod AuthMethod //The authentication method to use + /* Basic Auth Settings */ + BasicAuthCredentials []*BasicAuthCredentials //Basic auth credentials + BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target + BasicAuthGroupIDs []string //Group IDs that are allowed to access this endpoint + + /* Authelia Settings */ + AutheliaURL string //URL of the Authelia server, leave empty to use global settings e.g. authelia.example.com + UseHTTPS bool //Whether to use HTTPS for the Authelia server +} + +// A proxy endpoint record, a general interface for handling inbound routing +type ProxyEndpointv321 struct { + ProxyType ProxyType //The type of this proxy, see const def + RootOrMatchingDomain string //Matching domain for host, also act as key + MatchingDomainAlias []string //A list of domains that alias to this rule + ActiveOrigins []*loadbalance.Upstream //Activated Upstream or origin servers IP or domain to proxy to + InactiveOrigins []*loadbalance.Upstream //Disabled Upstream or origin servers IP or domain to proxy to + UseStickySession bool //Use stick session for load balancing + UseActiveLoadBalance bool //Use active loadbalancing, default passive + Disabled bool //If the rule is disabled + + //Inbound TLS/SSL Related + BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil) + + //Virtual Directories + VirtualDirectories []*VirtualDirectoryEndpoint + + //Custom Headers + HeaderRewriteRules *HeaderRewriteRules + EnableWebsocketCustomHeaders bool //Enable custom headers for websocket connections as well (default only http reqiests) + + //Authentication + AuthenticationProvider *AuthenticationProvider + + // Rate Limiting + RequireRateLimit bool + RateLimit int64 // Rate limit in requests per second + + //Uptime Monitor + DisableUptimeMonitor bool //Disable uptime monitor for this endpoint + + //Access Control + AccessFilterUUID string //Access filter ID + + //Fallback routing logic (Special Rule Sets Only) + DefaultSiteOption int //Fallback routing logic options + DefaultSiteValue string //Fallback routing target, optional + + //Internal Logic Elements + Tags []string // Tags for the proxy endpoint +} diff --git a/src/mod/update/v322/typedef322.go b/src/mod/update/v322/typedef322.go new file mode 100644 index 0000000..cd3ca2c --- /dev/null +++ b/src/mod/update/v322/typedef322.go @@ -0,0 +1,93 @@ +package v322 + +import "imuslab.com/zoraxy/mod/dynamicproxy/loadbalance" + +/* + + Authentication Provider in v3.2.2 + + The only change is the removal of the deprecated Authelia and Authentik SSO + provider, and the addition of the new Forward Auth provider. + + Need to map all provider with ID = 4 into 2 and remove the old provider configs +*/ + +type AuthMethod int + +/* +v3.2.1 Authentication Provider +const ( + AuthMethodNone AuthMethod = iota //No authentication required + AuthMethodBasic //Basic Auth + AuthMethodAuthelia //Authelia => 2 + AuthMethodOauth2 //Oauth2 + AuthMethodAuthentik //Authentik => 4 +) + +v3.2.2 Authentication Provider +const ( + AuthMethodNone AuthMethod = iota //No authentication required + AuthMethodBasic //Basic Auth + AuthMethodForward //Forward => 2 + AuthMethodOauth2 //Oauth2 +) + +We need to merge both Authelia and Authentik into the Forward Auth provider, and remove +*/ +//The updated structure of the authentication provider +type AuthenticationProviderV322 struct { + AuthMethod AuthMethod //The authentication method to use + /* Basic Auth Settings */ + BasicAuthCredentials []*BasicAuthCredentials //Basic auth credentials + BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target + BasicAuthGroupIDs []string //Group IDs that are allowed to access this endpoint + + /* Forward Auth Settings */ + ForwardAuthURL string // Full URL of the Forward Auth endpoint. Example: https://auth.example.com/api/authz/forward-auth + ForwardAuthResponseHeaders []string // List of headers to copy from the forward auth server response to the request. + ForwardAuthResponseClientHeaders []string // List of headers to copy from the forward auth server response to the client response. + ForwardAuthRequestHeaders []string // List of headers to copy from the original request to the auth server. If empty all are copied. + ForwardAuthRequestExcludedCookies []string // List of cookies to exclude from the request after sending it to the forward auth server. +} + +// A proxy endpoint record, a general interface for handling inbound routing +type ProxyEndpointv322 struct { + ProxyType ProxyType //The type of this proxy, see const def + RootOrMatchingDomain string //Matching domain for host, also act as key + MatchingDomainAlias []string //A list of domains that alias to this rule + ActiveOrigins []*loadbalance.Upstream //Activated Upstream or origin servers IP or domain to proxy to + InactiveOrigins []*loadbalance.Upstream //Disabled Upstream or origin servers IP or domain to proxy to + UseStickySession bool //Use stick session for load balancing + UseActiveLoadBalance bool //Use active loadbalancing, default passive + Disabled bool //If the rule is disabled + + //Inbound TLS/SSL Related + BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil) + + //Virtual Directories + VirtualDirectories []*VirtualDirectoryEndpoint + + //Custom Headers + HeaderRewriteRules *HeaderRewriteRules + EnableWebsocketCustomHeaders bool //Enable custom headers for websocket connections as well (default only http reqiests) + + //Authentication + AuthenticationProvider *AuthenticationProviderV322 + + // Rate Limiting + RequireRateLimit bool + RateLimit int64 // Rate limit in requests per second + + //Uptime Monitor + DisableUptimeMonitor bool //Disable uptime monitor for this endpoint + + //Access Control + AccessFilterUUID string //Access filter ID + + //Fallback routing logic (Special Rule Sets Only) + DefaultSiteOption int //Fallback routing logic options + DefaultSiteValue string //Fallback routing target, optional + + //Internal Logic Elements + Tags []string // Tags for the proxy endpoint +} diff --git a/src/mod/update/v322/v322.go b/src/mod/update/v322/v322.go new file mode 100644 index 0000000..a586193 --- /dev/null +++ b/src/mod/update/v322/v322.go @@ -0,0 +1,191 @@ +package v322 + +import ( + "encoding/json" + "fmt" + "log" + "os" + "path/filepath" + + "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy" + "imuslab.com/zoraxy/mod/dynamicproxy/rewrite" + "imuslab.com/zoraxy/mod/update/updateutil" +) + +// UpdateFrom321To322 updates proxy config files from v3.2.1 to v3.2.2 +func UpdateFrom321To322() error { + // Load the configs + oldConfigFiles, err := filepath.Glob("./conf/proxy/*.config") + if err != nil { + return err + } + + // Backup all the files + err = os.MkdirAll("./conf/proxy-321.old/", 0775) + if err != nil { + return err + } + + for _, oldConfigFile := range oldConfigFiles { + // Extract the file name from the path + fileName := filepath.Base(oldConfigFile) + // Construct the backup file path + backupFile := filepath.Join("./conf/proxy-321.old/", fileName) + + // Copy the file to the backup directory + err := updateutil.CopyFile(oldConfigFile, backupFile) + if err != nil { + return err + } + } + + // Read the config into the old struct + for _, oldConfigFile := range oldConfigFiles { + configContent, err := os.ReadFile(oldConfigFile) + if err != nil { + log.Println("Unable to read config file "+filepath.Base(oldConfigFile), err.Error()) + continue + } + + thisOldConfigStruct := ProxyEndpointv321{} + err = json.Unmarshal(configContent, &thisOldConfigStruct) + if err != nil { + log.Println("Unable to parse file "+filepath.Base(oldConfigFile), err.Error()) + continue + } + + // Convert the old struct to the new struct + thisNewConfigStruct := convertV321ToV322(thisOldConfigStruct) + + // Write the new config to file + newConfigContent, err := json.MarshalIndent(thisNewConfigStruct, "", " ") + if err != nil { + log.Println("Unable to marshal new config "+filepath.Base(oldConfigFile), err.Error()) + continue + } + + err = os.WriteFile(oldConfigFile, newConfigContent, 0664) + if err != nil { + log.Println("Unable to write new config "+filepath.Base(oldConfigFile), err.Error()) + continue + } + } + + return nil +} + +func convertV321ToV322(thisOldConfigStruct ProxyEndpointv321) ProxyEndpointv322 { + // Merge both Authelia and Authentik into the Forward Auth provider, and remove the old provider configs + if thisOldConfigStruct.AuthenticationProvider == nil { + //Configs before v3.1.7 with no authentication provider + // Set the default authentication provider + thisOldConfigStruct.AuthenticationProvider = &AuthenticationProvider{ + AuthMethod: AuthMethodNone, // Default to no authentication + BasicAuthCredentials: []*BasicAuthCredentials{}, + BasicAuthExceptionRules: []*BasicAuthExceptionRule{}, + BasicAuthGroupIDs: []string{}, + AutheliaURL: "", + UseHTTPS: false, + } + } else { + //Override the old authentication provider with the new one + if thisOldConfigStruct.AuthenticationProvider.AuthMethod == AuthMethodAuthelia { + thisOldConfigStruct.AuthenticationProvider.AuthMethod = 2 + } else if thisOldConfigStruct.AuthenticationProvider.AuthMethod == AuthMethodAuthentik { + thisOldConfigStruct.AuthenticationProvider.AuthMethod = 2 + } + + } + + if thisOldConfigStruct.AuthenticationProvider.BasicAuthGroupIDs == nil { + //Create an empty basic auth group IDs array if it does not exist + thisOldConfigStruct.AuthenticationProvider.BasicAuthGroupIDs = []string{} + } + + newAuthenticationProvider := AuthenticationProviderV322{ + AuthMethod: AuthMethodNone, // Default to no authentication + //Fill in the empty arrays + BasicAuthCredentials: []*BasicAuthCredentials{}, + BasicAuthExceptionRules: []*BasicAuthExceptionRule{}, + BasicAuthGroupIDs: []string{}, + ForwardAuthURL: "", + ForwardAuthResponseHeaders: []string{}, + ForwardAuthResponseClientHeaders: []string{}, + ForwardAuthRequestHeaders: []string{}, + ForwardAuthRequestExcludedCookies: []string{}, + } + + // In theory the old config should have a matching itoa value that + // can be converted to the new config + js, err := json.Marshal(thisOldConfigStruct.AuthenticationProvider) + if err != nil { + fmt.Println("Unable to marshal authentication provider "+thisOldConfigStruct.RootOrMatchingDomain, err.Error()) + fmt.Println("Using default authentication provider") + } + + err = json.Unmarshal(js, &newAuthenticationProvider) + if err != nil { + fmt.Println("Unable to unmarshal authentication provider "+thisOldConfigStruct.RootOrMatchingDomain, err.Error()) + fmt.Println("Using default authentication provider") + } else { + fmt.Println("Authentication provider for " + thisOldConfigStruct.RootOrMatchingDomain + " updated") + } + + // Fill in any null values in the old config struct + // these are non-upgrader requires values that updates between v3.1.5 to v3.2.1 + // will be in null state if not set by the user + if thisOldConfigStruct.VirtualDirectories == nil { + //Create an empty virtual directories array if it does not exist + thisOldConfigStruct.VirtualDirectories = []*VirtualDirectoryEndpoint{} + } + + if thisOldConfigStruct.HeaderRewriteRules == nil { + //Create an empty header rewrite rules array if it does not exist + thisOldConfigStruct.HeaderRewriteRules = &HeaderRewriteRules{ + UserDefinedHeaders: []*rewrite.UserDefinedHeader{}, + RequestHostOverwrite: "", + HSTSMaxAge: 0, + EnablePermissionPolicyHeader: false, + PermissionPolicy: permissionpolicy.GetDefaultPermissionPolicy(), + DisableHopByHopHeaderRemoval: false, + } + } + + if thisOldConfigStruct.Tags == nil { + //Create an empty tags array if it does not exist + thisOldConfigStruct.Tags = []string{} + } + + if thisOldConfigStruct.MatchingDomainAlias == nil { + //Create an empty matching domain alias array if it does not exist + thisOldConfigStruct.MatchingDomainAlias = []string{} + } + + // Update the config struct + thisNewConfigStruct := ProxyEndpointv322{ + ProxyType: thisOldConfigStruct.ProxyType, + RootOrMatchingDomain: thisOldConfigStruct.RootOrMatchingDomain, + MatchingDomainAlias: thisOldConfigStruct.MatchingDomainAlias, + ActiveOrigins: thisOldConfigStruct.ActiveOrigins, + InactiveOrigins: thisOldConfigStruct.InactiveOrigins, + UseStickySession: thisOldConfigStruct.UseStickySession, + UseActiveLoadBalance: thisOldConfigStruct.UseActiveLoadBalance, + Disabled: thisOldConfigStruct.Disabled, + BypassGlobalTLS: thisOldConfigStruct.BypassGlobalTLS, + VirtualDirectories: thisOldConfigStruct.VirtualDirectories, + HeaderRewriteRules: thisOldConfigStruct.HeaderRewriteRules, + EnableWebsocketCustomHeaders: thisOldConfigStruct.EnableWebsocketCustomHeaders, + RequireRateLimit: thisOldConfigStruct.RequireRateLimit, + RateLimit: thisOldConfigStruct.RateLimit, + DisableUptimeMonitor: thisOldConfigStruct.DisableUptimeMonitor, + AccessFilterUUID: thisOldConfigStruct.AccessFilterUUID, + DefaultSiteOption: thisOldConfigStruct.DefaultSiteOption, + DefaultSiteValue: thisOldConfigStruct.DefaultSiteValue, + Tags: thisOldConfigStruct.Tags, + } + + // Set the new authentication provider + thisNewConfigStruct.AuthenticationProvider = &newAuthenticationProvider + + return thisNewConfigStruct +} diff --git a/src/web/components/sso.html b/src/web/components/sso.html index a3d2f94..ee2a3ab 100644 --- a/src/web/components/sso.html +++ b/src/web/components/sso.html @@ -32,34 +32,40 @@ The full remote address or URL of the authorization servers forward auth endpoint. Example: https://auth.example.com/authz/forward-auth -
-
- - Advanced Options -
-
-
- - - Comma separated list of case-insensitive headers to copy from the authorization servers response to the request sent to the backend. If not set no headers are copied. Example: Remote-User,Remote-Groups,Remote-Email,Remote-Name +
+
+
+ + Advanced Options
-
- - - Comma separated list of case-insensitive headers to copy from the authorization servers response to the response sent to the client. If not set no headers are copied. Example: Set-Cookie,WWW-Authenticate -
-
- - - Comma separated list of case-insensitive headers to copy from the original request to the request made to the authorization server. If not set all headers are copied. Example: Cookie,Authorization -
-
- - - Comma separated list of case-sensitive cookie names to exclude from the request to the backend. If not set no cookies are excluded. Example: authelia_session,another_session +
+
+ + + Comma separated list of case-insensitive headers to copy from the authorization servers response to the request sent to the backend. If not set no headers are copied.
+ Example: Remote-User,Remote-Groups,Remote-Email,Remote-Name
+
+
+ + + Comma separated list of case-insensitive headers to copy from the authorization servers response to the response sent to the client. If not set no headers are copied.
+ Example: Set-Cookie,WWW-Authenticate
+
+
+ + + Comma separated list of case-insensitive headers to copy from the original request to the request made to the authorization server. If not set all headers are copied.
+ Example: Cookie,Authorization
+
+
+ + + Comma separated list of case-sensitive cookie names to exclude from the request to the backend. If not set no cookies are excluded.
+ Example: authelia_session,another_session
+
-

+