mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 23:57:21 +02:00
Added load balance origin picker
+ Added load balance picker + Added fallback mode for upstream + Added stick session
This commit is contained in:
parent
2aa35cbe6d
commit
aca6e44b35
20
src/main.go
20
src/main.go
@ -53,13 +53,13 @@ var enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high spee
|
|||||||
var staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters")
|
var staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters")
|
||||||
var allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
|
var allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
|
||||||
var logOutputToFile = flag.Bool("log", true, "Log terminal output to file")
|
var logOutputToFile = flag.Bool("log", true, "Log terminal output to file")
|
||||||
var updateMode = flag.Int("update", 0, "Version number (usually the version before you update Zoraxy) to start accumulation update. To update v3.0.7 to latest, use -update=307")
|
var enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
name = "Zoraxy"
|
name = "Zoraxy"
|
||||||
version = "3.0.8"
|
version = "3.0.8"
|
||||||
nodeUUID = "generic"
|
nodeUUID = "generic" //System uuid, in uuidv4 format
|
||||||
development = true //Set this to false to use embedded web fs
|
development = true //Set this to false to use embedded web fs
|
||||||
bootTime = time.Now().Unix()
|
bootTime = time.Now().Unix()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -123,8 +123,9 @@ func ShutdownSeq() {
|
|||||||
// Stop the mdns service
|
// Stop the mdns service
|
||||||
mdnsTickerStop <- true
|
mdnsTickerStop <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
mdnsScanner.Close()
|
mdnsScanner.Close()
|
||||||
|
fmt.Println("- Shutting down load balancer")
|
||||||
|
loadBalancer.Close()
|
||||||
fmt.Println("- Closing Certificates Auto Renewer")
|
fmt.Println("- Closing Certificates Auto Renewer")
|
||||||
acmeAutoRenewer.Close()
|
acmeAutoRenewer.Close()
|
||||||
//Remove the tmp folder
|
//Remove the tmp folder
|
||||||
@ -147,17 +148,16 @@ func main() {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *updateMode > 306 {
|
|
||||||
fmt.Println("Entering Update Mode")
|
|
||||||
update.RunConfigUpdate(*updateMode, update.GetVersionIntFromVersionNumber(version))
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !utils.ValidateListeningAddress(*webUIPort) {
|
if !utils.ValidateListeningAddress(*webUIPort) {
|
||||||
fmt.Println("Malformed -port (listening address) paramter. Do you mean -port=:" + *webUIPort + "?")
|
fmt.Println("Malformed -port (listening address) paramter. Do you mean -port=:" + *webUIPort + "?")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *enableAutoUpdate {
|
||||||
|
log.Println("[INFO] Checking required config update")
|
||||||
|
update.RunConfigUpdate(0, update.GetVersionIntFromVersionNumber(version))
|
||||||
|
}
|
||||||
|
|
||||||
SetupCloseHandler()
|
SetupCloseHandler()
|
||||||
|
|
||||||
//Read or create the system uuid
|
//Read or create the system uuid
|
||||||
|
@ -64,6 +64,7 @@ type ResponseRewriteRuleSet struct {
|
|||||||
PathPrefix string //Vdir prefix for root, / will be rewrite to this
|
PathPrefix string //Vdir prefix for root, / will be rewrite to this
|
||||||
UpstreamHeaders [][]string
|
UpstreamHeaders [][]string
|
||||||
DownstreamHeaders [][]string
|
DownstreamHeaders [][]string
|
||||||
|
NoRemoveHopByHop bool //Do not remove hop-by-hop headers, dangerous
|
||||||
Version string //Version number of Zoraxy, use for X-Proxy-By
|
Version string //Version number of Zoraxy, use for X-Proxy-By
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,18 +151,19 @@ func (router *Router) StartProxyService() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedUpstream, err := router.loadBalancer.GetRequestUpstreamTarget(r, sep.ActiveOrigins)
|
selectedUpstream, err := router.loadBalancer.GetRequestUpstreamTarget(w, r, sep.ActiveOrigins, sep.UseStickySession)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.ServeFile(w, r, "./web/hosterror.html")
|
http.ServeFile(w, r, "./web/hosterror.html")
|
||||||
log.Println(err.Error())
|
log.Println(err.Error())
|
||||||
router.logRequest(r, false, 404, "vdir-http", r.Host)
|
router.logRequest(r, false, 404, "vdir-http", r.Host)
|
||||||
}
|
}
|
||||||
selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||||
ProxyDomain: selectedUpstream.OriginIpOrDomain,
|
ProxyDomain: selectedUpstream.OriginIpOrDomain,
|
||||||
OriginalHost: originalHostHeader,
|
OriginalHost: originalHostHeader,
|
||||||
UseTLS: selectedUpstream.RequireTLS,
|
UseTLS: selectedUpstream.RequireTLS,
|
||||||
PathPrefix: "",
|
NoRemoveHopByHop: sep.DisableHopByHopHeaderRemoval,
|
||||||
Version: sep.parent.Option.HostVersion,
|
PathPrefix: "",
|
||||||
|
Version: sep.parent.Option.HostVersion,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ package loadbalance
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/gorilla/sessions"
|
||||||
"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
|
"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
"imuslab.com/zoraxy/mod/info/logger"
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
@ -17,12 +18,14 @@ import (
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
SystemUUID string //Use for the session store
|
||||||
UseActiveHealthCheck bool //Use active health check, default to false
|
UseActiveHealthCheck bool //Use active health check, default to false
|
||||||
Geodb *geodb.Store //GeoIP resolver for checking incoming request origin country
|
Geodb *geodb.Store //GeoIP resolver for checking incoming request origin country
|
||||||
Logger *logger.Logger
|
Logger *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type RouteManager struct {
|
type RouteManager struct {
|
||||||
|
SessionStore *sessions.CookieStore
|
||||||
LoadBalanceMap sync.Map //Sync map to store the last load balance state of a given node
|
LoadBalanceMap sync.Map //Sync map to store the last load balance state of a given node
|
||||||
OnlineStatusMap sync.Map //Sync map to store the online status of a given ip address or domain name
|
OnlineStatusMap sync.Map //Sync map to store the online status of a given ip address or domain name
|
||||||
onlineStatusTickerStop chan bool //Stopping channel for the online status pinger
|
onlineStatusTickerStop chan bool //Stopping channel for the online status pinger
|
||||||
@ -39,20 +42,26 @@ type Upstream struct {
|
|||||||
|
|
||||||
//Load balancing configs
|
//Load balancing configs
|
||||||
Weight int //Random weight for round robin, 0 for fallback only
|
Weight int //Random weight for round robin, 0 for fallback only
|
||||||
MaxConn int //Maxmium connection to this server, 0 for unlimited
|
MaxConn int //TODO: Maxmium connection to this server, 0 for unlimited
|
||||||
|
|
||||||
currentConnectionCounts atomic.Uint64 //Counter for number of client currently connected
|
//currentConnectionCounts atomic.Uint64 //Counter for number of client currently connected
|
||||||
proxy *dpcore.ReverseProxy
|
proxy *dpcore.ReverseProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new load balancer
|
// Create a new load balancer
|
||||||
func NewLoadBalancer(options *Options) *RouteManager {
|
func NewLoadBalancer(options *Options) *RouteManager {
|
||||||
onlineStatusCheckerStopChan := make(chan bool)
|
if options.SystemUUID == "" {
|
||||||
|
//System UUID not passed in. Use random key
|
||||||
|
options.SystemUUID = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generate a session store for stickySession
|
||||||
|
store := sessions.NewCookieStore([]byte(options.SystemUUID))
|
||||||
return &RouteManager{
|
return &RouteManager{
|
||||||
|
SessionStore: store,
|
||||||
LoadBalanceMap: sync.Map{},
|
LoadBalanceMap: sync.Map{},
|
||||||
OnlineStatusMap: sync.Map{},
|
OnlineStatusMap: sync.Map{},
|
||||||
onlineStatusTickerStop: onlineStatusCheckerStopChan,
|
onlineStatusTickerStop: nil,
|
||||||
Options: *options,
|
Options: *options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,6 @@ func (m *RouteManager) IsTargetOnline(matchingDomainOrIp string) bool {
|
|||||||
return ok && isOnline
|
return ok && isOnline
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RouteManager) SetTargetOffline() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ping a target to see if it is online
|
// Ping a target to see if it is online
|
||||||
func PingTarget(targetMatchingDomainOrIp string, requireTLS bool) bool {
|
func PingTarget(targetMatchingDomainOrIp string, requireTLS bool) bool {
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
@ -41,30 +37,3 @@ func PingTarget(targetMatchingDomainOrIp string, requireTLS bool) bool {
|
|||||||
|
|
||||||
return resp.StatusCode >= 200 && resp.StatusCode <= 600
|
return resp.StatusCode >= 200 && resp.StatusCode <= 600
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartHeartbeats start pinging each server every minutes to make sure all targets are online
|
|
||||||
// Active mode only
|
|
||||||
/*
|
|
||||||
func (m *RouteManager) StartHeartbeats(pingTargets []*FallbackProxyTarget) {
|
|
||||||
ticker := time.NewTicker(1 * time.Minute)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
fmt.Println("Heartbeat started")
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-m.onlineStatusTickerStop:
|
|
||||||
ticker.Stop()
|
|
||||||
return
|
|
||||||
case <-ticker.C:
|
|
||||||
for _, target := range pingTargets {
|
|
||||||
go func(target *FallbackProxyTarget) {
|
|
||||||
isOnline := PingTarget(target.MatchingDomainOrIp, target.RequireTLS)
|
|
||||||
m.LoadBalanceMap.Store(target.MatchingDomainOrIp, isOnline)
|
|
||||||
}(target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
@ -3,6 +3,8 @@ package loadbalance
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,12 +17,138 @@ import (
|
|||||||
|
|
||||||
// GetRequestUpstreamTarget return the upstream target where this
|
// GetRequestUpstreamTarget return the upstream target where this
|
||||||
// request should be routed
|
// request should be routed
|
||||||
func (m *RouteManager) GetRequestUpstreamTarget(r *http.Request, origins []*Upstream) (*Upstream, error) {
|
func (m *RouteManager) GetRequestUpstreamTarget(w http.ResponseWriter, r *http.Request, origins []*Upstream, useStickySession bool) (*Upstream, error) {
|
||||||
if len(origins) == 0 {
|
if len(origins) == 0 {
|
||||||
return nil, errors.New("no upstream is defined for this host")
|
return nil, errors.New("no upstream is defined for this host")
|
||||||
}
|
}
|
||||||
|
var targetOrigin = origins[0]
|
||||||
|
if useStickySession {
|
||||||
|
//Use stick session, check which origins this request previously used
|
||||||
|
targetOriginId, err := m.getSessionHandler(r, origins)
|
||||||
|
if err != nil {
|
||||||
|
//No valid session found. Assign a new upstream
|
||||||
|
targetOrigin, index, err := getRandomUpstreamByWeight(origins)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Oops. Unable to get random upstream")
|
||||||
|
targetOrigin = origins[0]
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
m.setSessionHandler(w, r, targetOrigin.OriginIpOrDomain, index)
|
||||||
|
return targetOrigin, nil
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: Add upstream picking algorithm here
|
//Valid session found. Resume the previous session
|
||||||
fmt.Println("DEBUG: Picking origin " + origins[0].OriginIpOrDomain)
|
return origins[targetOriginId], nil
|
||||||
return origins[0], nil
|
} else {
|
||||||
|
//Do not use stick session. Get a random one
|
||||||
|
var err error
|
||||||
|
targetOrigin, _, err = getRandomUpstreamByWeight(origins)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
targetOrigin = origins[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Println("DEBUG: Picking origin " + targetOrigin.OriginIpOrDomain)
|
||||||
|
return targetOrigin, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Features related to session access */
|
||||||
|
//Set a new origin for this connection by session
|
||||||
|
func (m *RouteManager) setSessionHandler(w http.ResponseWriter, r *http.Request, originIpOrDomain string, index int) error {
|
||||||
|
session, err := m.SessionStore.Get(r, "STICKYSESSION")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session.Values["zr_sid_origin"] = originIpOrDomain
|
||||||
|
session.Values["zr_sid_index"] = index
|
||||||
|
session.Options.MaxAge = 86400 //1 day
|
||||||
|
session.Options.Path = "/"
|
||||||
|
err = session.Save(r, w)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the previous connected origin from session
|
||||||
|
func (m *RouteManager) getSessionHandler(r *http.Request, upstreams []*Upstream) (int, error) {
|
||||||
|
// Get existing session
|
||||||
|
session, err := m.SessionStore.Get(r, "STICKYSESSION")
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve session values for origin
|
||||||
|
originDomainRaw := session.Values["zr_sid_origin"]
|
||||||
|
originIDRaw := session.Values["zr_sid_index"]
|
||||||
|
|
||||||
|
if originDomainRaw == nil || originIDRaw == nil {
|
||||||
|
return -1, errors.New("no session has been set")
|
||||||
|
}
|
||||||
|
originDomain := originDomainRaw.(string)
|
||||||
|
originID := originIDRaw.(int)
|
||||||
|
|
||||||
|
//Check if it has been modified
|
||||||
|
if len(upstreams) < originID || upstreams[originID].OriginIpOrDomain != originDomain {
|
||||||
|
//Mismatch or upstreams has been updated
|
||||||
|
return -1, errors.New("upstreams has been changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return originID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Functions related to random upstream picking */
|
||||||
|
// Get a random upstream by the weights defined in Upstream struct, return the upstream, index value and any error
|
||||||
|
func getRandomUpstreamByWeight(upstreams []*Upstream) (*Upstream, int, error) {
|
||||||
|
var ret *Upstream
|
||||||
|
sum := 0
|
||||||
|
for _, c := range upstreams {
|
||||||
|
sum += c.Weight
|
||||||
|
}
|
||||||
|
r, err := intRange(0, sum)
|
||||||
|
if err != nil {
|
||||||
|
return ret, -1, err
|
||||||
|
}
|
||||||
|
counter := 0
|
||||||
|
for _, c := range upstreams {
|
||||||
|
r -= c.Weight
|
||||||
|
if r < 0 {
|
||||||
|
return c, counter, nil
|
||||||
|
}
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret == nil {
|
||||||
|
//All fallback
|
||||||
|
//use the first one that is with weight = 0
|
||||||
|
fallbackUpstreams := []*Upstream{}
|
||||||
|
fallbackUpstreamsOriginalID := []int{}
|
||||||
|
for ix, upstream := range upstreams {
|
||||||
|
if upstream.Weight == 0 {
|
||||||
|
fallbackUpstreams = append(fallbackUpstreams, upstream)
|
||||||
|
fallbackUpstreamsOriginalID = append(fallbackUpstreamsOriginalID, ix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upstreamID := rand.Intn(len(fallbackUpstreams))
|
||||||
|
return fallbackUpstreams[upstreamID], fallbackUpstreamsOriginalID[upstreamID], nil
|
||||||
|
}
|
||||||
|
return ret, -1, errors.New("failed to pick an upstream origin server")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntRange returns a random integer in the range from min to max.
|
||||||
|
func intRange(min, max int) (int, error) {
|
||||||
|
var result int
|
||||||
|
switch {
|
||||||
|
case min > max:
|
||||||
|
// Fail with error
|
||||||
|
return result, errors.New("min is greater than max")
|
||||||
|
case max == min:
|
||||||
|
result = max
|
||||||
|
case max > min:
|
||||||
|
b := rand.Intn(max-min) + min
|
||||||
|
result = min + int(b)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ func (router *Router) rewriteURL(rooturl string, requestURL string) string {
|
|||||||
func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
|
func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) {
|
||||||
r.Header.Set("X-Forwarded-Host", r.Host)
|
r.Header.Set("X-Forwarded-Host", r.Host)
|
||||||
r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID)
|
r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID)
|
||||||
selectedUpstream, err := h.Parent.loadBalancer.GetRequestUpstreamTarget(r, target.ActiveOrigins)
|
selectedUpstream, err := h.Parent.loadBalancer.GetRequestUpstreamTarget(w, r, target.ActiveOrigins, target.UseStickySession)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.ServeFile(w, r, "./web/rperror.html")
|
http.ServeFile(w, r, "./web/rperror.html")
|
||||||
log.Println(err.Error())
|
log.Println(err.Error())
|
||||||
@ -164,6 +164,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
PathPrefix: "",
|
PathPrefix: "",
|
||||||
UpstreamHeaders: upstreamHeaders,
|
UpstreamHeaders: upstreamHeaders,
|
||||||
DownstreamHeaders: downstreamHeaders,
|
DownstreamHeaders: downstreamHeaders,
|
||||||
|
NoRemoveHopByHop: target.DisableHopByHopHeaderRemoval,
|
||||||
Version: target.parent.Option.HostVersion,
|
Version: target.parent.Option.HostVersion,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -133,6 +133,7 @@ type ProxyEndpoint struct {
|
|||||||
HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers
|
HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers
|
||||||
EnablePermissionPolicyHeader bool //Enable injection of permission policy header
|
EnablePermissionPolicyHeader bool //Enable injection of permission policy header
|
||||||
PermissionPolicy *permissionpolicy.PermissionsPolicy //Permission policy header
|
PermissionPolicy *permissionpolicy.PermissionsPolicy //Permission policy header
|
||||||
|
DisableHopByHopHeaderRemoval bool //TODO: Do not remove hop-by-hop headers
|
||||||
|
|
||||||
//Authentication
|
//Authentication
|
||||||
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
||||||
|
@ -9,21 +9,52 @@ package update
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
v308 "imuslab.com/zoraxy/mod/update/v308"
|
v308 "imuslab.com/zoraxy/mod/update/v308"
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run config update. Version numbers are int. For example
|
// Run config update. Version numbers are int. For example
|
||||||
// to update 3.0.7 to 3.0.8, use RunConfigUpdate(307, 308)
|
// to update 3.0.7 to 3.0.8, use RunConfigUpdate(307, 308)
|
||||||
// This function support cross versions updates (e.g. 307 -> 310)
|
// This function support cross versions updates (e.g. 307 -> 310)
|
||||||
func RunConfigUpdate(fromVersion int, toVersion int) {
|
func RunConfigUpdate(fromVersion int, toVersion int) {
|
||||||
|
versionFile := "./conf/version"
|
||||||
|
if fromVersion == 0 {
|
||||||
|
//Run auto previous version detection
|
||||||
|
fromVersion = 307
|
||||||
|
if utils.FileExists(versionFile) {
|
||||||
|
//Read the version file
|
||||||
|
previousVersionText, err := os.ReadFile(versionFile)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unable to read version file at " + versionFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert the version to int
|
||||||
|
versionInt, err := strconv.Atoi(strings.TrimSpace(string(previousVersionText)))
|
||||||
|
if err != nil {
|
||||||
|
panic("Unable to read version file at " + versionFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fromVersion = versionInt
|
||||||
|
}
|
||||||
|
|
||||||
|
if fromVersion == toVersion {
|
||||||
|
//No need to update
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Do iterate update
|
||||||
for i := fromVersion; i < toVersion; i++ {
|
for i := fromVersion; i < toVersion; i++ {
|
||||||
oldVersion := fromVersion
|
oldVersion := fromVersion
|
||||||
newVersion := fromVersion + 1
|
newVersion := fromVersion + 1
|
||||||
fmt.Println("Updating from v", oldVersion, " to v", newVersion)
|
fmt.Println("Updating from v", oldVersion, " to v", newVersion)
|
||||||
runUpdateRoutineWithVersion(oldVersion, newVersion)
|
runUpdateRoutineWithVersion(oldVersion, newVersion)
|
||||||
|
//Write the updated version to file
|
||||||
|
os.WriteFile(versionFile, []byte(strconv.Itoa(newVersion)), 0775)
|
||||||
}
|
}
|
||||||
fmt.Println("Update completed")
|
fmt.Println("Update completed")
|
||||||
}
|
}
|
||||||
@ -36,6 +67,7 @@ func GetVersionIntFromVersionNumber(version string) int {
|
|||||||
|
|
||||||
func runUpdateRoutineWithVersion(fromVersion int, toVersion int) {
|
func runUpdateRoutineWithVersion(fromVersion int, toVersion int) {
|
||||||
if fromVersion == 307 && toVersion == 308 {
|
if fromVersion == 307 && toVersion == 308 {
|
||||||
|
//Updating from v3.0.7 to v3.0.8
|
||||||
err := v308.UpdateFrom307To308()
|
err := v308.UpdateFrom307To308()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -207,12 +207,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
useBypassGlobalTLS := bypassGlobalTLS == "true"
|
useBypassGlobalTLS := bypassGlobalTLS == "true"
|
||||||
|
|
||||||
//Enable TLS validation?
|
//Enable TLS validation?
|
||||||
stv, _ := utils.PostPara(r, "tlsval")
|
skipTlsValidation, _ := utils.PostBool(r, "tlsval")
|
||||||
if stv == "" {
|
|
||||||
stv = "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
skipTlsValidation := (stv == "true")
|
|
||||||
|
|
||||||
//Get access rule ID
|
//Get access rule ID
|
||||||
accessRuleID, _ := utils.PostPara(r, "access")
|
accessRuleID, _ := utils.PostPara(r, "access")
|
||||||
@ -225,12 +220,10 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Require basic auth?
|
// Require basic auth?
|
||||||
rba, _ := utils.PostPara(r, "bauth")
|
requireBasicAuth, _ := utils.PostBool(r, "bauth")
|
||||||
if rba == "" {
|
|
||||||
rba = "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
requireBasicAuth := (rba == "true")
|
//Use sticky session?
|
||||||
|
useStickySession, _ := utils.PostBool(r, "stickysess")
|
||||||
|
|
||||||
// Require Rate Limiting?
|
// Require Rate Limiting?
|
||||||
requireRateLimit := false
|
requireRateLimit := false
|
||||||
@ -328,7 +321,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
InactiveOrigins: []*loadbalance.Upstream{},
|
InactiveOrigins: []*loadbalance.Upstream{},
|
||||||
UseStickySession: false, //TODO: Move options to webform
|
UseStickySession: useStickySession,
|
||||||
|
|
||||||
//TLS
|
//TLS
|
||||||
BypassGlobalTLS: useBypassGlobalTLS,
|
BypassGlobalTLS: useBypassGlobalTLS,
|
||||||
|
@ -104,8 +104,9 @@ func startupSequence() {
|
|||||||
|
|
||||||
//Create a load balancer
|
//Create a load balancer
|
||||||
loadBalancer = loadbalance.NewLoadBalancer(&loadbalance.Options{
|
loadBalancer = loadbalance.NewLoadBalancer(&loadbalance.Options{
|
||||||
Geodb: geodbStore,
|
SystemUUID: nodeUUID,
|
||||||
Logger: SystemWideLogger,
|
Geodb: geodbStore,
|
||||||
|
Logger: SystemWideLogger,
|
||||||
})
|
})
|
||||||
|
|
||||||
//Create the access controller
|
//Create the access controller
|
||||||
|
@ -11,6 +11,17 @@
|
|||||||
border-radius: 0.6em;
|
border-radius: 0.6em;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.descheader{
|
||||||
|
display:none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1367px) {
|
||||||
|
.descheader{
|
||||||
|
display:auto !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<div class="ui stackable grid">
|
<div class="ui stackable grid">
|
||||||
@ -47,16 +58,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Access Rule</label>
|
<div class="ui checkbox">
|
||||||
<div class="ui selection dropdown">
|
<input type="checkbox" id="useStickySessionLB">
|
||||||
<input type="hidden" id="newProxyRuleAccessFilter" value="default">
|
<label>Sticky Session<br><small>Enable stick session on upstream load balancing</small></label>
|
||||||
<i class="dropdown icon"></i>
|
|
||||||
<div class="default text">Default</div>
|
|
||||||
<div class="menu" id="newProxyRuleAccessList">
|
|
||||||
<div class="item" data-value="default"><i class="ui yellow star icon"></i> Default</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<small>Allow regional access control using blacklist or whitelist. Use "default" for "allow all".</small>
|
</div>
|
||||||
|
<div class="ui horizontal divider">
|
||||||
|
<i class="ui green lock icon"></i>
|
||||||
|
Security
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
@ -76,27 +85,21 @@
|
|||||||
<label>Allow plain HTTP access<br><small>Allow this subdomain to be connected without TLS (Require HTTP server enabled on port 80)</small></label>
|
<label>Allow plain HTTP access<br><small>Allow this subdomain to be connected without TLS (Require HTTP server enabled on port 80)</small></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="ui horizontal divider">
|
||||||
<div class="ui checkbox">
|
<i class="ui red ban icon"></i>
|
||||||
<input type="checkbox" id="useStickySessionLB">
|
Access Control
|
||||||
<label>Sticky Session<br><small>Enable stick session on upstream load balancing</small></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<label>Access Rule</label>
|
||||||
<input type="checkbox" id="requireRateLimit">
|
<div class="ui selection dropdown">
|
||||||
<label>Require Rate Limit<br><small>This proxy endpoint will be rate limited.</small></label>
|
<input type="hidden" id="newProxyRuleAccessFilter" value="default">
|
||||||
</div>
|
<i class="dropdown icon"></i>
|
||||||
</div>
|
<div class="default text">Default</div>
|
||||||
<div class="field">
|
<div class="menu" id="newProxyRuleAccessList">
|
||||||
<label>Rate Limit</label>
|
<div class="item" data-value="default"><i class="ui yellow star icon"></i> Default</div>
|
||||||
<div class="ui fluid right labeled input">
|
|
||||||
<input type="number" id="proxyRateLimit" placeholder="100" min="1" max="1000" value="100">
|
|
||||||
<div class="ui basic label">
|
|
||||||
req / sec / IP
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<small>Return a 429 error code if request rate exceed the rate limit.</small>
|
<small>Allow regional access control using blacklist or whitelist. Use "default" for "allow all".</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
@ -131,6 +134,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="requireRateLimit">
|
||||||
|
<label>Require Rate Limit<br><small>This proxy endpoint will be rate limited.</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Rate Limit</label>
|
||||||
|
<div class="ui fluid right labeled input">
|
||||||
|
<input type="number" id="proxyRateLimit" placeholder="100" min="1" max="1000" value="100">
|
||||||
|
<div class="ui basic label">
|
||||||
|
req / sec / IP
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small>Return a 429 error code if request rate exceed the rate limit.</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -166,17 +185,18 @@
|
|||||||
|
|
||||||
//New Proxy Endpoint
|
//New Proxy Endpoint
|
||||||
function newProxyEndpoint(){
|
function newProxyEndpoint(){
|
||||||
var rootname = $("#rootname").val();
|
let rootname = $("#rootname").val();
|
||||||
var proxyDomain = $("#proxyDomain").val();
|
let proxyDomain = $("#proxyDomain").val();
|
||||||
var useTLS = $("#reqTls")[0].checked;
|
let useTLS = $("#reqTls")[0].checked;
|
||||||
var skipTLSValidation = $("#skipTLSValidation")[0].checked;
|
let skipTLSValidation = $("#skipTLSValidation")[0].checked;
|
||||||
var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked;
|
let bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked;
|
||||||
var requireBasicAuth = $("#requireBasicAuth")[0].checked;
|
let requireBasicAuth = $("#requireBasicAuth")[0].checked;
|
||||||
var proxyRateLimit = $("#proxyRateLimit").val();
|
let proxyRateLimit = $("#proxyRateLimit").val();
|
||||||
var requireRateLimit = $("#requireRateLimit")[0].checked;
|
let requireRateLimit = $("#requireRateLimit")[0].checked;
|
||||||
var skipWebSocketOriginCheck = $("#skipWebsocketOriginCheck")[0].checked;
|
let skipWebSocketOriginCheck = $("#skipWebsocketOriginCheck")[0].checked;
|
||||||
var accessRuleToUse = $("#newProxyRuleAccessFilter").val();
|
let accessRuleToUse = $("#newProxyRuleAccessFilter").val();
|
||||||
|
let useStickySessionLB = $("#useStickySessionLB")[0].checked;
|
||||||
|
|
||||||
if (rootname.trim() == ""){
|
if (rootname.trim() == ""){
|
||||||
$("#rootname").parent().addClass("error");
|
$("#rootname").parent().addClass("error");
|
||||||
return
|
return
|
||||||
@ -207,6 +227,7 @@
|
|||||||
ratenum: proxyRateLimit,
|
ratenum: proxyRateLimit,
|
||||||
cred: JSON.stringify(credentials),
|
cred: JSON.stringify(credentials),
|
||||||
access: accessRuleToUse,
|
access: accessRuleToUse,
|
||||||
|
stickysess: useStickySessionLB,
|
||||||
},
|
},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user