mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-01 13:17:21 +02:00
Fixed #318
- Added support for automatic X-Remote-User header when basic auth is enabled - Moved header logic to rewrite module (new module) - Added default site automatic fix for URL missing http:// or https:// prefix
This commit is contained in:
parent
99295cad86
commit
f98e04a9fc
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
// 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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
src/mod/dynamicproxy/rewrite/rewrite.go
Normal file
79
src/mod/dynamicproxy/rewrite/rewrite.go
Normal file
@ -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
src/mod/dynamicproxy/rewrite/typedef.go
Normal file
34
src/mod/dynamicproxy/rewrite/typedef.go
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user