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
+