Browse Source

Added support for X-Remote-User

Toby Chui 4 months ago
parent
commit
97c79a2d31

+ 6 - 2
mod/dynamicproxy/Server.go

@@ -173,7 +173,6 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
 		fallthrough
 	case DefaultSite_ReverseProxy:
 		//They both share the same behavior
-
 		//Check if any virtual directory rules matches
 		proxyingPath := strings.TrimSpace(r.RequestURI)
 		targetProxyEndpoint := proot.GetVirtualDirectoryHandlerFromRequestURI(proxyingPath)
@@ -198,8 +197,13 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
 			redirectTarget = "about:blank"
 		}
 
+		//Check if the default site values start with http or https
+		if !strings.HasPrefix(redirectTarget, "http://") && !strings.HasPrefix(redirectTarget, "https://") {
+			redirectTarget = "http://" + redirectTarget
+		}
+
 		//Check if it is an infinite loopback redirect
-		parsedURL, err := url.Parse(proot.DefaultSiteValue)
+		parsedURL, err := url.Parse(redirectTarget)
 		if err != nil {
 			//Error when parsing target. Send to root
 			h.hostRequest(w, r, h.Parent.Root)

+ 3 - 0
mod/dynamicproxy/basicAuth.go

@@ -49,6 +49,9 @@ func handleBasicAuth(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint)
 	for _, cred := range pe.BasicAuthCredentials {
 		if u == cred.Username && hashedPassword == cred.PasswordHash {
 			matchingFound = true
+
+			//Set the X-Remote-User header
+			r.Header.Set("X-Remote-User", u)
 			break
 		}
 	}

+ 4 - 72
mod/dynamicproxy/customHeader.go

@@ -1,80 +1,12 @@
 package dynamicproxy
 
-import (
-	"strconv"
-
-	"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
-)
-
 /*
 	CustomHeader.go
 
 	This script handle parsing and injecting custom headers
 	into the dpcore routing logic
-*/
-
-// SplitInboundOutboundHeaders split user defined headers into upstream and downstream headers
-// return upstream header and downstream header key-value pairs
-// if the header is expected to be deleted, the value will be set to empty string
-func (ept *ProxyEndpoint) SplitInboundOutboundHeaders() ([][]string, [][]string) {
-	if len(ept.UserDefinedHeaders) == 0 && ept.HSTSMaxAge == 0 && !ept.EnablePermissionPolicyHeader {
-		//Early return if there are no defined headers
-		return [][]string{}, [][]string{}
-	}
-
-	//Use pre-allocation for faster performance
-	//Downstream +2 for Permission Policy and HSTS
-	upstreamHeaders := make([][]string, len(ept.UserDefinedHeaders))
-	downstreamHeaders := make([][]string, len(ept.UserDefinedHeaders)+2)
-	upstreamHeaderCounter := 0
-	downstreamHeaderCounter := 0
-
-	//Sort the headers into upstream or downstream
-	for _, customHeader := range ept.UserDefinedHeaders {
-		thisHeaderSet := make([]string, 2)
-		thisHeaderSet[0] = customHeader.Key
-		thisHeaderSet[1] = customHeader.Value
-		if customHeader.IsRemove {
-			//Prevent invalid config
-			thisHeaderSet[1] = ""
-		}
 
-		//Assign to slice
-		if customHeader.Direction == HeaderDirection_ZoraxyToUpstream {
-			upstreamHeaders[upstreamHeaderCounter] = thisHeaderSet
-			upstreamHeaderCounter++
-		} else if customHeader.Direction == HeaderDirection_ZoraxyToDownstream {
-			downstreamHeaders[downstreamHeaderCounter] = thisHeaderSet
-			downstreamHeaderCounter++
-		}
-	}
-
-	//Check if the endpoint require HSTS headers
-	if ept.HSTSMaxAge > 0 {
-		if ept.ContainsWildcardName(true) {
-			//Endpoint listening domain includes wildcards.
-			downstreamHeaders[downstreamHeaderCounter] = []string{"Strict-Transport-Security", "max-age=" + strconv.Itoa(int(ept.HSTSMaxAge)) + "; includeSubdomains"}
-		} else {
-			downstreamHeaders[downstreamHeaderCounter] = []string{"Strict-Transport-Security", "max-age=" + strconv.Itoa(int(ept.HSTSMaxAge))}
-		}
-
-		downstreamHeaderCounter++
-	}
-
-	//Check if the endpoint require Permission Policy
-	if ept.EnablePermissionPolicyHeader {
-		var usingPermissionPolicy *permissionpolicy.PermissionsPolicy
-		if ept.PermissionPolicy != nil {
-			//Custom permission policy
-			usingPermissionPolicy = ept.PermissionPolicy
-		} else {
-			//Permission policy is enabled but not customized. Use default
-			usingPermissionPolicy = permissionpolicy.GetDefaultPermissionPolicy()
-		}
-
-		downstreamHeaders[downstreamHeaderCounter] = usingPermissionPolicy.ToKeyValueHeader()
-		downstreamHeaderCounter++
-	}
-
-	return upstreamHeaders, downstreamHeaders
-}
+	Updates: 2024-10-26
+	Contents from this file has been moved to rewrite/rewrite.go
+	This file is kept for contributors to understand the structure
+*/

+ 3 - 2
mod/dynamicproxy/endpoints.go

@@ -8,6 +8,7 @@ import (
 	"golang.org/x/text/cases"
 	"golang.org/x/text/language"
 	"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
+	"imuslab.com/zoraxy/mod/dynamicproxy/rewrite"
 )
 
 /*
@@ -36,7 +37,7 @@ func (ep *ProxyEndpoint) UserDefinedHeaderExists(key string) bool {
 
 // Remvoe a user defined header from the list
 func (ep *ProxyEndpoint) RemoveUserDefinedHeader(key string) error {
-	newHeaderList := []*UserDefinedHeader{}
+	newHeaderList := []*rewrite.UserDefinedHeader{}
 	for _, header := range ep.UserDefinedHeaders {
 		if !strings.EqualFold(header.Key, key) {
 			newHeaderList = append(newHeaderList, header)
@@ -49,7 +50,7 @@ func (ep *ProxyEndpoint) RemoveUserDefinedHeader(key string) error {
 }
 
 // Add a user defined header to the list, duplicates will be automatically removed
-func (ep *ProxyEndpoint) AddUserDefinedHeader(newHeaderRule *UserDefinedHeader) error {
+func (ep *ProxyEndpoint) AddUserDefinedHeader(newHeaderRule *rewrite.UserDefinedHeader) error {
 	if ep.UserDefinedHeaderExists(newHeaderRule.Key) {
 		ep.RemoveUserDefinedHeader(newHeaderRule.Key)
 	}

+ 18 - 3
mod/dynamicproxy/proxyRequestHandler.go

@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
+	"imuslab.com/zoraxy/mod/dynamicproxy/rewrite"
 	"imuslab.com/zoraxy/mod/netutils"
 	"imuslab.com/zoraxy/mod/statistic"
 	"imuslab.com/zoraxy/mod/websocketproxy"
@@ -159,8 +160,15 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
 	}
 
 	//Build downstream and upstream header rules
-	upstreamHeaders, downstreamHeaders := target.SplitInboundOutboundHeaders()
+	upstreamHeaders, downstreamHeaders := rewrite.SplitUpDownStreamHeaders(&rewrite.HeaderRewriteOptions{
+		UserDefinedHeaders:           target.UserDefinedHeaders,
+		HSTSMaxAge:                   target.HSTSMaxAge,
+		HSTSIncludeSubdomains:        target.ContainsWildcardName(true),
+		EnablePermissionPolicyHeader: target.EnablePermissionPolicyHeader,
+		PermissionPolicy:             target.PermissionPolicy,
+	})
 
+	//Handle the request reverse proxy
 	statusCode, err := selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
 		ProxyDomain:         selectedUpstream.OriginIpOrDomain,
 		OriginalHost:        originalHostHeader,
@@ -226,9 +234,16 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
 		r.URL, _ = url.Parse(originalHostHeader)
 	}
 
-	//Build downstream and upstream header rules
-	upstreamHeaders, downstreamHeaders := target.parent.SplitInboundOutboundHeaders()
+	//Build downstream and upstream header rules, use the parent (subdomain) endpoint's headers
+	upstreamHeaders, downstreamHeaders := rewrite.SplitUpDownStreamHeaders(&rewrite.HeaderRewriteOptions{
+		UserDefinedHeaders:           target.parent.UserDefinedHeaders,
+		HSTSMaxAge:                   target.parent.HSTSMaxAge,
+		HSTSIncludeSubdomains:        target.parent.ContainsWildcardName(true),
+		EnablePermissionPolicyHeader: target.parent.EnablePermissionPolicyHeader,
+		PermissionPolicy:             target.parent.PermissionPolicy,
+	})
 
+	//Handle the virtual directory reverse proxy request
 	statusCode, err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
 		ProxyDomain:         target.Domain,
 		OriginalHost:        originalHostHeader,

+ 79 - 0
mod/dynamicproxy/rewrite/rewrite.go

@@ -0,0 +1,79 @@
+package rewrite
+
+/*
+ rewrite.go
+
+ This script handle the rewrite logic for custom headers
+*/
+
+import (
+	"strconv"
+
+	"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
+)
+
+// SplitInboundOutboundHeaders split user defined headers into upstream and downstream headers
+// return upstream header and downstream header key-value pairs
+// if the header is expected to be deleted, the value will be set to empty string
+func SplitUpDownStreamHeaders(rewriteOptions *HeaderRewriteOptions) ([][]string, [][]string) {
+	if len(rewriteOptions.UserDefinedHeaders) == 0 && rewriteOptions.HSTSMaxAge == 0 && !rewriteOptions.EnablePermissionPolicyHeader {
+		//Early return if there are no defined headers
+		return [][]string{}, [][]string{}
+	}
+
+	//Use pre-allocation for faster performance
+	//Downstream +2 for Permission Policy and HSTS
+	upstreamHeaders := make([][]string, len(rewriteOptions.UserDefinedHeaders))
+	downstreamHeaders := make([][]string, len(rewriteOptions.UserDefinedHeaders)+2)
+	upstreamHeaderCounter := 0
+	downstreamHeaderCounter := 0
+
+	//Sort the headers into upstream or downstream
+	for _, customHeader := range rewriteOptions.UserDefinedHeaders {
+		thisHeaderSet := make([]string, 2)
+		thisHeaderSet[0] = customHeader.Key
+		thisHeaderSet[1] = customHeader.Value
+		if customHeader.IsRemove {
+			//Prevent invalid config
+			thisHeaderSet[1] = ""
+		}
+
+		//Assign to slice
+		if customHeader.Direction == HeaderDirection_ZoraxyToUpstream {
+			upstreamHeaders[upstreamHeaderCounter] = thisHeaderSet
+			upstreamHeaderCounter++
+		} else if customHeader.Direction == HeaderDirection_ZoraxyToDownstream {
+			downstreamHeaders[downstreamHeaderCounter] = thisHeaderSet
+			downstreamHeaderCounter++
+		}
+	}
+
+	//Check if the endpoint require HSTS headers
+	if rewriteOptions.HSTSMaxAge > 0 {
+		if rewriteOptions.HSTSIncludeSubdomains {
+			//Endpoint listening domain includes wildcards.
+			downstreamHeaders[downstreamHeaderCounter] = []string{"Strict-Transport-Security", "max-age=" + strconv.Itoa(int(rewriteOptions.HSTSMaxAge)) + "; includeSubdomains"}
+		} else {
+			downstreamHeaders[downstreamHeaderCounter] = []string{"Strict-Transport-Security", "max-age=" + strconv.Itoa(int(rewriteOptions.HSTSMaxAge))}
+		}
+
+		downstreamHeaderCounter++
+	}
+
+	//Check if the endpoint require Permission Policy
+	if rewriteOptions.EnablePermissionPolicyHeader {
+		var usingPermissionPolicy *permissionpolicy.PermissionsPolicy
+		if rewriteOptions.PermissionPolicy != nil {
+			//Custom permission policy
+			usingPermissionPolicy = rewriteOptions.PermissionPolicy
+		} else {
+			//Permission policy is enabled but not customized. Use default
+			usingPermissionPolicy = permissionpolicy.GetDefaultPermissionPolicy()
+		}
+
+		downstreamHeaders[downstreamHeaderCounter] = usingPermissionPolicy.ToKeyValueHeader()
+		downstreamHeaderCounter++
+	}
+
+	return upstreamHeaders, downstreamHeaders
+}

+ 34 - 0
mod/dynamicproxy/rewrite/typedef.go

@@ -0,0 +1,34 @@
+package rewrite
+
+import "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
+
+/*
+	typdef.go
+
+	This script handle the type definition for custom headers
+*/
+
+/* Custom Header Related Data structure */
+// Header injection direction type
+type HeaderDirection int
+
+const (
+	HeaderDirection_ZoraxyToUpstream   HeaderDirection = 0 //Inject (or remove) header to request out-going from Zoraxy to backend server
+	HeaderDirection_ZoraxyToDownstream HeaderDirection = 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 UserDefinedHeader struct {
+	Direction HeaderDirection
+	Key       string
+	Value     string
+	IsRemove  bool //Instead of set, remove this key instead
+}
+
+type HeaderRewriteOptions struct {
+	UserDefinedHeaders           []*UserDefinedHeader                //Custom headers to append when proxying requests from this endpoint
+	HSTSMaxAge                   int64                               //HSTS max age, set to 0 for disable HSTS headers
+	HSTSIncludeSubdomains        bool                                //Include subdomains in HSTS header
+	EnablePermissionPolicyHeader bool                                //Enable injection of permission policy header
+	PermissionPolicy             *permissionpolicy.PermissionsPolicy //Permission policy header
+}

+ 2 - 18
mod/dynamicproxy/typedef.go

@@ -12,6 +12,7 @@ import (
 	"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
 	"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
 	"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
+	"imuslab.com/zoraxy/mod/dynamicproxy/rewrite"
 	"imuslab.com/zoraxy/mod/geodb"
 	"imuslab.com/zoraxy/mod/info/logger"
 	"imuslab.com/zoraxy/mod/statistic"
@@ -84,23 +85,6 @@ type BasicAuthExceptionRule struct {
 	PathPrefix string
 }
 
-/* Custom Header Related Data structure */
-// Header injection direction type
-type HeaderDirection int
-
-const (
-	HeaderDirection_ZoraxyToUpstream   HeaderDirection = 0 //Inject (or remove) header to request out-going from Zoraxy to backend server
-	HeaderDirection_ZoraxyToDownstream HeaderDirection = 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 UserDefinedHeader struct {
-	Direction HeaderDirection
-	Key       string
-	Value     string
-	IsRemove  bool //Instead of set, remove this key instead
-}
-
 /* Routing Rule Data Structures */
 
 // A Virtual Directory endpoint, provide a subset of ProxyEndpoint for better
@@ -133,7 +117,7 @@ type ProxyEndpoint struct {
 	VirtualDirectories []*VirtualDirectoryEndpoint
 
 	//Custom Headers
-	UserDefinedHeaders           []*UserDefinedHeader                //Custom headers to append when proxying requests from this endpoint
+	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

+ 7 - 6
reverseproxy.go

@@ -13,6 +13,7 @@ import (
 	"imuslab.com/zoraxy/mod/dynamicproxy"
 	"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
 	"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
+	"imuslab.com/zoraxy/mod/dynamicproxy/rewrite"
 	"imuslab.com/zoraxy/mod/uptime"
 	"imuslab.com/zoraxy/mod/utils"
 )
@@ -332,7 +333,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
 			//VDir
 			VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
 			//Custom headers
-			UserDefinedHeaders: []*dynamicproxy.UserDefinedHeader{},
+			UserDefinedHeaders: []*rewrite.UserDefinedHeader{},
 			//Auth
 			RequireBasicAuth:        requireBasicAuth,
 			BasicAuthCredentials:    basicAuthCredentials,
@@ -1127,7 +1128,7 @@ func HandleCustomHeaderList(w http.ResponseWriter, r *http.Request) {
 	//List all custom headers
 	customHeaderList := targetProxyEndpoint.UserDefinedHeaders
 	if customHeaderList == nil {
-		customHeaderList = []*dynamicproxy.UserDefinedHeader{}
+		customHeaderList = []*rewrite.UserDefinedHeader{}
 	}
 	js, _ := json.Marshal(customHeaderList)
 	utils.SendJSONResponse(w, string(js))
@@ -1173,11 +1174,11 @@ func HandleCustomHeaderAdd(w http.ResponseWriter, r *http.Request) {
 	}
 
 	//Create a Custom Header Defination type
-	var rewriteDirection dynamicproxy.HeaderDirection
+	var rewriteDirection rewrite.HeaderDirection
 	if direction == "toOrigin" {
-		rewriteDirection = dynamicproxy.HeaderDirection_ZoraxyToUpstream
+		rewriteDirection = rewrite.HeaderDirection_ZoraxyToUpstream
 	} else if direction == "toClient" {
-		rewriteDirection = dynamicproxy.HeaderDirection_ZoraxyToDownstream
+		rewriteDirection = rewrite.HeaderDirection_ZoraxyToDownstream
 	} else {
 		//Unknown direction
 		utils.SendErrorResponse(w, "header rewrite direction not supported")
@@ -1188,7 +1189,7 @@ func HandleCustomHeaderAdd(w http.ResponseWriter, r *http.Request) {
 	if rewriteType == "remove" {
 		isRemove = true
 	}
-	headerRewriteDefination := dynamicproxy.UserDefinedHeader{
+	headerRewriteDefination := rewrite.UserDefinedHeader{
 		Key:       name,
 		Value:     value,
 		Direction: rewriteDirection,