Browse Source

Added support for custom header variables

Toby Chui 4 months ago
parent
commit
c504d98e08

+ 8 - 2
mod/dynamicproxy/proxyRequestHandler.go

@@ -159,9 +159,12 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
 		r.URL, _ = url.Parse(originalHostHeader)
 	}
 
+	//Populate the user-defined headers with the values from the request
+	rewrittenUserDefinedHeaders := rewrite.PopulateRequestHeaderVariables(r, target.UserDefinedHeaders)
+
 	//Build downstream and upstream header rules
 	upstreamHeaders, downstreamHeaders := rewrite.SplitUpDownStreamHeaders(&rewrite.HeaderRewriteOptions{
-		UserDefinedHeaders:           target.UserDefinedHeaders,
+		UserDefinedHeaders:           rewrittenUserDefinedHeaders,
 		HSTSMaxAge:                   target.HSTSMaxAge,
 		HSTSIncludeSubdomains:        target.ContainsWildcardName(true),
 		EnablePermissionPolicyHeader: target.EnablePermissionPolicyHeader,
@@ -234,9 +237,12 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
 		r.URL, _ = url.Parse(originalHostHeader)
 	}
 
+	//Populate the user-defined headers with the values from the request
+	rewrittenUserDefinedHeaders := rewrite.PopulateRequestHeaderVariables(r, target.parent.UserDefinedHeaders)
+
 	//Build downstream and upstream header rules, use the parent (subdomain) endpoint's headers
 	upstreamHeaders, downstreamHeaders := rewrite.SplitUpDownStreamHeaders(&rewrite.HeaderRewriteOptions{
-		UserDefinedHeaders:           target.parent.UserDefinedHeaders,
+		UserDefinedHeaders:           rewrittenUserDefinedHeaders,
 		HSTSMaxAge:                   target.parent.HSTSMaxAge,
 		HSTSIncludeSubdomains:        target.parent.ContainsWildcardName(true),
 		EnablePermissionPolicyHeader: target.parent.EnablePermissionPolicyHeader,

+ 63 - 0
mod/dynamicproxy/rewrite/headervars.go

@@ -0,0 +1,63 @@
+package rewrite
+
+import (
+	"fmt"
+	"net/http"
+	"strings"
+)
+
+// GetHeaderVariableValuesFromRequest returns a map of header variables and their values
+// note that variables behavior is not exactly identical to nginx variables
+func GetHeaderVariableValuesFromRequest(r *http.Request) map[string]string {
+	vars := make(map[string]string)
+
+	// Request-specific variables
+	vars["$host"] = r.Host
+	vars["$remote_addr"] = r.RemoteAddr
+	vars["$request_uri"] = r.RequestURI
+	vars["$request_method"] = r.Method
+	vars["$content_length"] = fmt.Sprintf("%d", r.ContentLength)
+	vars["$content_type"] = r.Header.Get("Content-Type")
+
+	// Parsed URI elements
+	vars["$uri"] = r.URL.Path
+	vars["$args"] = r.URL.RawQuery
+	vars["$scheme"] = r.URL.Scheme
+	vars["$query_string"] = r.URL.RawQuery
+
+	// User agent and referer
+	vars["$http_user_agent"] = r.UserAgent()
+	vars["$http_referer"] = r.Referer()
+
+	return vars
+}
+
+// CustomHeadersIncludeDynamicVariables checks if the user-defined headers contain dynamic variables
+// use for early exit when processing the headers
+func CustomHeadersIncludeDynamicVariables(userDefinedHeaders []*UserDefinedHeader) bool {
+	for _, header := range userDefinedHeaders {
+		if strings.Contains(header.Value, "$") {
+			return true
+		}
+	}
+	return false
+}
+
+// PopulateRequestHeaderVariables populates the user-defined headers with the values from the request
+func PopulateRequestHeaderVariables(r *http.Request, userDefinedHeaders []*UserDefinedHeader) []*UserDefinedHeader {
+	if !CustomHeadersIncludeDynamicVariables(userDefinedHeaders) {
+		// Early exit if there are no dynamic variables
+		return userDefinedHeaders
+	}
+	vars := GetHeaderVariableValuesFromRequest(r)
+	populatedHeaders := []*UserDefinedHeader{}
+	// Populate the user-defined headers with the values from the request
+	for _, header := range userDefinedHeaders {
+		thisHeader := header.Copy()
+		for key, value := range vars {
+			thisHeader.Value = strings.ReplaceAll(thisHeader.Value, key, value)
+		}
+		populatedHeaders = append(populatedHeaders, thisHeader)
+	}
+	return populatedHeaders
+}

+ 18 - 1
mod/dynamicproxy/rewrite/typedef.go

@@ -1,6 +1,10 @@
 package rewrite
 
-import "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
+import (
+	"encoding/json"
+
+	"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
+)
 
 /*
 	typdef.go
@@ -32,3 +36,16 @@ type HeaderRewriteOptions struct {
 	EnablePermissionPolicyHeader bool                                //Enable injection of permission policy header
 	PermissionPolicy             *permissionpolicy.PermissionsPolicy //Permission policy header
 }
+
+// Utilities for header rewrite
+func (h *UserDefinedHeader) GetDirection() HeaderDirection {
+	return h.Direction
+}
+
+// Copy eturns a deep copy of the UserDefinedHeader
+func (h *UserDefinedHeader) Copy() *UserDefinedHeader {
+	result := UserDefinedHeader{}
+	js, _ := json.Marshal(h)
+	json.Unmarshal(js, &result)
+	return &result
+}

+ 1 - 0
start.go

@@ -318,6 +318,7 @@ func startupSequence() {
 		SystemWideLogger.PrintAndLog("warning", "Invalid start flag combination: docker=true && runtime.GOOS == windows. Running in docker UX development mode.", nil)
 	}
 	DockerUXOptimizer = dockerux.NewDockerOptimizer(*runningInDocker, SystemWideLogger)
+
 }
 
 // This sequence start after everything is initialized