Added load balancer (wip)

+ Added support for multiple upstreams
+ Added load balancer
+ Added upstream abstraction in endpoint
+ Added load balancer structure
+ Added breaking change auto-updater
+ Added uptime monitor proxy type definitions
+ Added upstream editor UI
+ Fixed charset bug in many snippets HTML files
This commit is contained in:
Toby Chui
2024-07-01 21:17:20 +08:00
parent 7e62fef879
commit 2aa35cbe6d
46 changed files with 1908 additions and 351 deletions

44
src/mod/update/update.go Normal file
View File

@@ -0,0 +1,44 @@
package update
/*
Update.go
This module handle cross version updates that contains breaking changes
update command should always exit after the update is completed
*/
import (
"fmt"
"strconv"
"strings"
v308 "imuslab.com/zoraxy/mod/update/v308"
)
// Run config update. Version numbers are int. For example
// to update 3.0.7 to 3.0.8, use RunConfigUpdate(307, 308)
// This function support cross versions updates (e.g. 307 -> 310)
func RunConfigUpdate(fromVersion int, toVersion int) {
for i := fromVersion; i < toVersion; i++ {
oldVersion := fromVersion
newVersion := fromVersion + 1
fmt.Println("Updating from v", oldVersion, " to v", newVersion)
runUpdateRoutineWithVersion(oldVersion, newVersion)
}
fmt.Println("Update completed")
}
func GetVersionIntFromVersionNumber(version string) int {
versionNumberOnly := strings.ReplaceAll(version, ".", "")
versionInt, _ := strconv.Atoi(versionNumberOnly)
return versionInt
}
func runUpdateRoutineWithVersion(fromVersion int, toVersion int) {
if fromVersion == 307 && toVersion == 308 {
err := v308.UpdateFrom307To308()
if err != nil {
panic(err)
}
}
}

View File

@@ -0,0 +1,138 @@
package v308
/*
v307 type definations
This file wrap up the self-contained data structure
for v3.0.7 structure and allow automatic updates
for future releases if required
*/
type v307PermissionsPolicy struct {
Accelerometer []string `json:"accelerometer"`
AmbientLightSensor []string `json:"ambient_light_sensor"`
Autoplay []string `json:"autoplay"`
Battery []string `json:"battery"`
Camera []string `json:"camera"`
CrossOriginIsolated []string `json:"cross_origin_isolated"`
DisplayCapture []string `json:"display_capture"`
DocumentDomain []string `json:"document_domain"`
EncryptedMedia []string `json:"encrypted_media"`
ExecutionWhileNotRendered []string `json:"execution_while_not_rendered"`
ExecutionWhileOutOfView []string `json:"execution_while_out_of_viewport"`
Fullscreen []string `json:"fullscreen"`
Geolocation []string `json:"geolocation"`
Gyroscope []string `json:"gyroscope"`
KeyboardMap []string `json:"keyboard_map"`
Magnetometer []string `json:"magnetometer"`
Microphone []string `json:"microphone"`
Midi []string `json:"midi"`
NavigationOverride []string `json:"navigation_override"`
Payment []string `json:"payment"`
PictureInPicture []string `json:"picture_in_picture"`
PublicKeyCredentialsGet []string `json:"publickey_credentials_get"`
ScreenWakeLock []string `json:"screen_wake_lock"`
SyncXHR []string `json:"sync_xhr"`
USB []string `json:"usb"`
WebShare []string `json:"web_share"`
XRSpatialTracking []string `json:"xr_spatial_tracking"`
ClipboardRead []string `json:"clipboard_read"`
ClipboardWrite []string `json:"clipboard_write"`
Gamepad []string `json:"gamepad"`
SpeakerSelection []string `json:"speaker_selection"`
ConversionMeasurement []string `json:"conversion_measurement"`
FocusWithoutUserActivation []string `json:"focus_without_user_activation"`
HID []string `json:"hid"`
IdleDetection []string `json:"idle_detection"`
InterestCohort []string `json:"interest_cohort"`
Serial []string `json:"serial"`
SyncScript []string `json:"sync_script"`
TrustTokenRedemption []string `json:"trust_token_redemption"`
Unload []string `json:"unload"`
WindowPlacement []string `json:"window_placement"`
VerticalScroll []string `json:"vertical_scroll"`
}
// Auth credential for basic auth on certain endpoints
type v307BasicAuthCredentials struct {
Username string
PasswordHash string
}
// Auth credential for basic auth on certain endpoints
type v307BasicAuthUnhashedCredentials struct {
Username string
Password string
}
// Paths to exclude in basic auth enabled proxy handler
type v307BasicAuthExceptionRule struct {
PathPrefix string
}
// Header injection direction type
type v307HeaderDirection int
const (
HeaderDirection_ZoraxyToUpstream v307HeaderDirection = 0 //Inject (or remove) header to request out-going from Zoraxy to backend server
HeaderDirection_ZoraxyToDownstream v307HeaderDirection = 1 //Inject (or remove) header to request out-going from Zoraxy to client (e.g. browser)
)
// User defined headers to add into a proxy endpoint
type v307UserDefinedHeader struct {
Direction v307HeaderDirection
Key string
Value string
IsRemove bool //Instead of set, remove this key instead
}
// The original proxy endpoint structure from v3.0.7
type v307ProxyEndpoint struct {
ProxyType int //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
Domain string //Domain or IP to proxy to
//TLS/SSL Related
RequireTLS bool //Target domain require TLS
BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil)
SkipCertValidations bool //Set to true to accept self signed certs
SkipWebSocketOriginCheck bool //Skip origin check on websocket upgrade connections
//Virtual Directories
VirtualDirectories []*v307VirtualDirectoryEndpoint
//Custom Headers
UserDefinedHeaders []*v307UserDefinedHeader //Custom headers to append when proxying requests from this endpoint
HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers
EnablePermissionPolicyHeader bool //Enable injection of permission policy header
PermissionPolicy *v307PermissionsPolicy //Permission policy header
//Authentication
RequireBasicAuth bool //Set to true to request basic auth before proxy
BasicAuthCredentials []*v307BasicAuthCredentials //Basic auth credentials
BasicAuthExceptionRules []*v307BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target
// Rate Limiting
RequireRateLimit bool
RateLimit int64 // Rate limit in requests per second
//Access Control
AccessFilterUUID string //Access filter ID
Disabled bool //If the rule is disabled
//Fallback routing logic (Special Rule Sets Only)
DefaultSiteOption int //Fallback routing logic options
DefaultSiteValue string //Fallback routing target, optional
}
// A Virtual Directory endpoint, provide a subset of ProxyEndpoint for better
// program structure than directly using ProxyEndpoint
type v307VirtualDirectoryEndpoint 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
}

View File

@@ -0,0 +1,63 @@
package v308
/*
v308 type definations
This file wrap up the self-contained data structure
for v3.0.8 structure and allow automatic updates
for future releases if required
Some struct are identical as v307 and hence it is not redefined here
*/
/* Upstream or Origin Server */
type v308Upstream struct {
//Upstream Proxy Configs
OriginIpOrDomain string //Target IP address or domain name with port
RequireTLS bool //Require TLS connection
SkipCertValidations bool //Set to true to accept self signed certs
SkipWebSocketOriginCheck bool //Skip origin check on websocket upgrade connections
//Load balancing configs
Weight int //Prirotiy of fallback, set all to 0 for round robin
MaxConn int //Maxmium connection to this server
}
// A proxy endpoint record, a general interface for handling inbound routing
type v308ProxyEndpoint struct {
ProxyType int //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 []*v308Upstream //Activated Upstream or origin servers IP or domain to proxy to
InactiveOrigins []*v308Upstream //Disabled Upstream or origin servers IP or domain to proxy to
UseStickySession bool //Use stick session for load balancing
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 []*v307VirtualDirectoryEndpoint
//Custom Headers
UserDefinedHeaders []*v307UserDefinedHeader //Custom headers to append when proxying requests from this endpoint
HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers
EnablePermissionPolicyHeader bool //Enable injection of permission policy header
PermissionPolicy *v307PermissionsPolicy //Permission policy header
//Authentication
RequireBasicAuth bool //Set to true to request basic auth before proxy
BasicAuthCredentials []*v307BasicAuthCredentials //Basic auth credentials
BasicAuthExceptionRules []*v307BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target
// Rate Limiting
RequireRateLimit bool
RateLimit int64 // Rate limit in requests per second
//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
}

132
src/mod/update/v308/v308.go Normal file
View File

@@ -0,0 +1,132 @@
package v308
import (
"encoding/json"
"io"
"log"
"os"
"path/filepath"
)
/*
v3.0.7 update to v3.0.8
This update function
*/
// Update proxy config files from v3.0.7 to v3.0.8
func UpdateFrom307To308() 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.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.old/", fileName)
// Copy the file to the backup directory
err := 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 := v307ProxyEndpoint{}
err = json.Unmarshal(configContent, &thisOldConfigStruct)
if err != nil {
log.Println("Unable to parse file "+filepath.Base(oldConfigFile), err.Error())
continue
}
//Convert the old config to new config
newProxyStructure := convertV307ToV308(thisOldConfigStruct)
js, _ := json.MarshalIndent(newProxyStructure, "", " ")
err = os.WriteFile(oldConfigFile, js, 0775)
if err != nil {
log.Println(err.Error())
continue
}
}
return nil
}
func convertV307ToV308(old v307ProxyEndpoint) v308ProxyEndpoint {
// Create a new v308ProxyEndpoint instance
matchingDomainsSlice := old.MatchingDomainAlias
if matchingDomainsSlice == nil {
matchingDomainsSlice = []string{}
}
newEndpoint := v308ProxyEndpoint{
ProxyType: old.ProxyType,
RootOrMatchingDomain: old.RootOrMatchingDomain,
MatchingDomainAlias: matchingDomainsSlice,
ActiveOrigins: []*v308Upstream{{ // Mapping Domain field to v308Upstream struct
OriginIpOrDomain: old.Domain,
RequireTLS: old.RequireTLS,
SkipCertValidations: old.SkipCertValidations,
SkipWebSocketOriginCheck: old.SkipWebSocketOriginCheck,
Weight: 1,
MaxConn: 0,
}},
InactiveOrigins: []*v308Upstream{},
UseStickySession: false,
Disabled: old.Disabled,
BypassGlobalTLS: old.BypassGlobalTLS,
VirtualDirectories: old.VirtualDirectories,
UserDefinedHeaders: old.UserDefinedHeaders,
HSTSMaxAge: old.HSTSMaxAge,
EnablePermissionPolicyHeader: old.EnablePermissionPolicyHeader,
PermissionPolicy: old.PermissionPolicy,
RequireBasicAuth: old.RequireBasicAuth,
BasicAuthCredentials: old.BasicAuthCredentials,
BasicAuthExceptionRules: old.BasicAuthExceptionRules,
RequireRateLimit: old.RequireRateLimit,
RateLimit: old.RateLimit,
AccessFilterUUID: old.AccessFilterUUID,
DefaultSiteOption: old.DefaultSiteOption,
DefaultSiteValue: old.DefaultSiteValue,
}
return newEndpoint
}
// Helper function to copy files
func copyFile(src, dst string) error {
sourceFile, err := os.Open(src)
if err != nil {
return err
}
defer sourceFile.Close()
destinationFile, err := os.Create(dst)
if err != nil {
return err
}
defer destinationFile.Close()
_, err = io.Copy(destinationFile, sourceFile)
return err
}