diff --git a/src/api.go b/src/api.go
index b9e86e8..0d2f7c9 100644
--- a/src/api.go
+++ b/src/api.go
@@ -77,6 +77,8 @@ func initAPIs() {
authRouter.HandleFunc("/api/proxy/header/add", HandleCustomHeaderAdd)
authRouter.HandleFunc("/api/proxy/header/remove", HandleCustomHeaderRemove)
authRouter.HandleFunc("/api/proxy/header/handleHSTS", HandleHSTSState)
+ authRouter.HandleFunc("/api/proxy/header/handleHopByHop", HandleHopByHop)
+ authRouter.HandleFunc("/api/proxy/header/handleHostOverwrite", HandleHostOverwrite)
authRouter.HandleFunc("/api/proxy/header/handlePermissionPolicy", HandlePermissionPolicy)
//Reverse proxy auth related APIs
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
diff --git a/src/main.go b/src/main.go
index bcd49ec..fc933a5 100644
--- a/src/main.go
+++ b/src/main.go
@@ -60,7 +60,7 @@ var (
name = "Zoraxy"
version = "3.1.0"
nodeUUID = "generic" //System uuid, in uuidv4 format
- development = false //Set this to false to use embedded web fs
+ development = true //Set this to false to use embedded web fs
bootTime = time.Now().Unix()
/*
diff --git a/src/mod/dynamicproxy/dpcore/dpcore.go b/src/mod/dynamicproxy/dpcore/dpcore.go
index 2c64eaa..c659435 100644
--- a/src/mod/dynamicproxy/dpcore/dpcore.go
+++ b/src/mod/dynamicproxy/dpcore/dpcore.go
@@ -57,6 +57,7 @@ type ReverseProxy struct {
}
type ResponseRewriteRuleSet struct {
+ /* Basic Rewrite Rulesets */
ProxyDomain string
OriginalHost string
UseTLS bool
@@ -64,8 +65,13 @@ type ResponseRewriteRuleSet struct {
PathPrefix string //Vdir prefix for root, / will be rewrite to this
UpstreamHeaders [][]string
DownstreamHeaders [][]string
- NoRemoveHopByHop bool //Do not remove hop-by-hop headers, dangerous
- Version string //Version number of Zoraxy, use for X-Proxy-By
+
+ /* Advance Usecase Options */
+ HostHeaderOverwrite string //Force overwrite of request "Host" header (advanced usecase)
+ NoRemoveHopByHop bool //Do not remove hop-by-hop headers (advanced usecase)
+
+ /* System Information Payload */
+ Version string //Version number of Zoraxy, use for X-Proxy-By
}
type requestCanceler interface {
@@ -73,8 +79,8 @@ type requestCanceler interface {
}
type DpcoreOptions struct {
- IgnoreTLSVerification bool
- FlushInterval time.Duration
+ IgnoreTLSVerification bool //Disable all TLS verification when request pass through this proxy router
+ FlushInterval time.Duration //Duration to flush in normal requests. Stream request or keep-alive request will always flush with interval of -1 (immediately)
}
func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOptions) *ReverseProxy {
@@ -281,7 +287,10 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
outreq.Close = false
//Only skip origin rewrite iff proxy target require TLS and it is external domain name like github.com
- if !(rrr.UseTLS && isExternalDomainName(rrr.ProxyDomain)) {
+ if rrr.HostHeaderOverwrite != "" {
+ //Use user defined overwrite header value, see issue #255
+ outreq.Host = rrr.HostHeaderOverwrite
+ } else if !(rrr.UseTLS && isExternalDomainName(rrr.ProxyDomain)) {
// Always use the original host, see issue #164
outreq.Host = rrr.OriginalHost
}
@@ -291,7 +300,9 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
copyHeader(outreq.Header, req.Header)
// Remove hop-by-hop headers.
- removeHeaders(outreq.Header, rrr.NoCache)
+ if !rrr.NoRemoveHopByHop {
+ removeHeaders(outreq.Header, rrr.NoCache)
+ }
// Add X-Forwarded-For Header.
addXForwardedForHeader(outreq)
@@ -313,7 +324,9 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
}
// Remove hop-by-hop headers listed in the "Connection" header of the response, Remove hop-by-hop headers.
- removeHeaders(res.Header, rrr.NoCache)
+ if !rrr.NoRemoveHopByHop {
+ removeHeaders(res.Header, rrr.NoCache)
+ }
//Remove the User-Agent header if exists
if _, ok := res.Header["User-Agent"]; ok {
diff --git a/src/mod/dynamicproxy/dynamicproxy.go b/src/mod/dynamicproxy/dynamicproxy.go
index 4f6e6a8..17f8523 100644
--- a/src/mod/dynamicproxy/dynamicproxy.go
+++ b/src/mod/dynamicproxy/dynamicproxy.go
@@ -158,12 +158,13 @@ func (router *Router) StartProxyService() error {
router.logRequest(r, false, 404, "vdir-http", r.Host)
}
selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
- ProxyDomain: selectedUpstream.OriginIpOrDomain,
- OriginalHost: originalHostHeader,
- UseTLS: selectedUpstream.RequireTLS,
- NoRemoveHopByHop: sep.DisableHopByHopHeaderRemoval,
- PathPrefix: "",
- Version: sep.parent.Option.HostVersion,
+ ProxyDomain: selectedUpstream.OriginIpOrDomain,
+ OriginalHost: originalHostHeader,
+ UseTLS: selectedUpstream.RequireTLS,
+ HostHeaderOverwrite: sep.RequestHostOverwrite,
+ NoRemoveHopByHop: sep.DisableHopByHopHeaderRemoval,
+ PathPrefix: "",
+ Version: sep.parent.Option.HostVersion,
})
return
}
diff --git a/src/mod/dynamicproxy/proxyRequestHandler.go b/src/mod/dynamicproxy/proxyRequestHandler.go
index 389fb03..f4ae58d 100644
--- a/src/mod/dynamicproxy/proxyRequestHandler.go
+++ b/src/mod/dynamicproxy/proxyRequestHandler.go
@@ -157,15 +157,16 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
upstreamHeaders, downstreamHeaders := target.SplitInboundOutboundHeaders()
err = selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
- ProxyDomain: selectedUpstream.OriginIpOrDomain,
- OriginalHost: originalHostHeader,
- UseTLS: selectedUpstream.RequireTLS,
- NoCache: h.Parent.Option.NoCache,
- PathPrefix: "",
- UpstreamHeaders: upstreamHeaders,
- DownstreamHeaders: downstreamHeaders,
- NoRemoveHopByHop: target.DisableHopByHopHeaderRemoval,
- Version: target.parent.Option.HostVersion,
+ ProxyDomain: selectedUpstream.OriginIpOrDomain,
+ OriginalHost: originalHostHeader,
+ UseTLS: selectedUpstream.RequireTLS,
+ NoCache: h.Parent.Option.NoCache,
+ PathPrefix: "",
+ UpstreamHeaders: upstreamHeaders,
+ DownstreamHeaders: downstreamHeaders,
+ HostHeaderOverwrite: target.RequestHostOverwrite,
+ NoRemoveHopByHop: target.DisableHopByHopHeaderRemoval,
+ Version: target.parent.Option.HostVersion,
})
var dnsError *net.DNSError
@@ -224,13 +225,14 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
upstreamHeaders, downstreamHeaders := target.parent.SplitInboundOutboundHeaders()
err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
- ProxyDomain: target.Domain,
- OriginalHost: originalHostHeader,
- UseTLS: target.RequireTLS,
- PathPrefix: target.MatchingPath,
- UpstreamHeaders: upstreamHeaders,
- DownstreamHeaders: downstreamHeaders,
- Version: target.parent.parent.Option.HostVersion,
+ ProxyDomain: target.Domain,
+ OriginalHost: originalHostHeader,
+ UseTLS: target.RequireTLS,
+ PathPrefix: target.MatchingPath,
+ UpstreamHeaders: upstreamHeaders,
+ DownstreamHeaders: downstreamHeaders,
+ HostHeaderOverwrite: target.parent.RequestHostOverwrite,
+ Version: target.parent.parent.Option.HostVersion,
})
var dnsError *net.DNSError
diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go
index 2effa6c..16ebd45 100644
--- a/src/mod/dynamicproxy/typedef.go
+++ b/src/mod/dynamicproxy/typedef.go
@@ -132,6 +132,7 @@ type ProxyEndpoint struct {
//Custom Headers
UserDefinedHeaders []*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
PermissionPolicy *permissionpolicy.PermissionsPolicy //Permission policy header
diff --git a/src/reverseproxy.go b/src/reverseproxy.go
index 90a9f77..cea0c50 100644
--- a/src/reverseproxy.go
+++ b/src/reverseproxy.go
@@ -1235,6 +1235,149 @@ func HandleCustomHeaderRemove(w http.ResponseWriter, r *http.Request) {
}
+func HandleHostOverwrite(w http.ResponseWriter, r *http.Request) {
+ domain, err := utils.PostPara(r, "domain")
+ if err != nil {
+ domain, err = utils.GetPara(r, "domain")
+ if err != nil {
+ utils.SendErrorResponse(w, "domain or matching rule not defined")
+ return
+ }
+ }
+ //Get the proxy endpoint object dedicated to this domain
+ targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
+ if err != nil {
+ utils.SendErrorResponse(w, "target endpoint not exists")
+ return
+ }
+
+ if r.Method == http.MethodGet {
+ //Get the current host header
+ js, _ := json.Marshal(targetProxyEndpoint.RequestHostOverwrite)
+ utils.SendJSONResponse(w, string(js))
+ } else if r.Method == http.MethodPost {
+ //Set the new host header
+ newHostname, _ := utils.PostPara(r, "hostname")
+
+ //As this will require change in the proxy instance we are running
+ //we need to clone and respawn this proxy endpoint
+ newProxyEndpoint := targetProxyEndpoint.Clone()
+ newProxyEndpoint.RequestHostOverwrite = newHostname
+ //Save proxy endpoint
+ err = SaveReverseProxyConfig(newProxyEndpoint)
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Spawn a new endpoint with updated dpcore
+ preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Remove the old endpoint
+ err = targetProxyEndpoint.Remove()
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Add the newly prepared endpoint to runtime
+ err = dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint)
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Print log message
+ if newHostname != "" {
+ SystemWideLogger.Println("Updated " + domain + " hostname overwrite to: " + newHostname)
+ } else {
+ SystemWideLogger.Println("Removed " + domain + " hostname overwrite")
+ }
+
+ utils.SendOK(w)
+ } else {
+ //Invalid method
+ http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
+ }
+}
+
+// HandleHopByHop get and set the hop by hop remover state
+// note that it shows the DISABLE STATE of hop-by-hop remover, not the enable state
+func HandleHopByHop(w http.ResponseWriter, r *http.Request) {
+ domain, err := utils.PostPara(r, "domain")
+ if err != nil {
+ domain, err = utils.GetPara(r, "domain")
+ if err != nil {
+ utils.SendErrorResponse(w, "domain or matching rule not defined")
+ return
+ }
+ }
+
+ targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
+ if err != nil {
+ utils.SendErrorResponse(w, "target endpoint not exists")
+ return
+ }
+
+ if r.Method == http.MethodGet {
+ //Get the current hop by hop header state
+ js, _ := json.Marshal(!targetProxyEndpoint.DisableHopByHopHeaderRemoval)
+ utils.SendJSONResponse(w, string(js))
+ } else if r.Method == http.MethodPost {
+ //Set the hop by hop header state
+ enableHopByHopRemover, _ := utils.PostBool(r, "removeHopByHop")
+
+ //As this will require change in the proxy instance we are running
+ //we need to clone and respawn this proxy endpoint
+ newProxyEndpoint := targetProxyEndpoint.Clone()
+ //Storage file use false as default, so disable removal = not enable remover
+ targetProxyEndpoint.DisableHopByHopHeaderRemoval = !enableHopByHopRemover
+ //Save proxy endpoint
+ err = SaveReverseProxyConfig(newProxyEndpoint)
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Spawn a new endpoint with updated dpcore
+ preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Remove the old endpoint
+ err = targetProxyEndpoint.Remove()
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Add the newly prepared endpoint to runtime
+ err = dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint)
+ if err != nil {
+ utils.SendErrorResponse(w, err.Error())
+ return
+ }
+
+ //Print log message
+ if enableHopByHopRemover {
+ SystemWideLogger.Println("Enabled hop-by-hop headers removal on " + domain)
+ } else {
+ SystemWideLogger.Println("Disabled hop-by-hop headers removal on " + domain)
+ }
+
+ utils.SendOK(w)
+
+ } else {
+ http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
+ }
+}
+
// Handle view or edit HSTS states
func HandleHSTSState(w http.ResponseWriter, r *http.Request) {
domain, err := utils.PostPara(r, "domain")
diff --git a/src/web/snippet/customHeaders.html b/src/web/snippet/customHeaders.html
index b995638..522e08a 100644
--- a/src/web/snippet/customHeaders.html
+++ b/src/web/snippet/customHeaders.html
@@ -83,6 +83,41 @@
+
+
+
+
+
+ Advance Settings
+
+
+
+
+
Settings in this section are for advanced users. Invalid settings might cause werid, unexpected behavior.
+
+
+
Overwrite Host Header
+
Manual override the automatic "Host" header rewrite logic. Leave empty for automatic.
+
+
+
+
+
+
+
+
Remove Hop-by-hop Headers
+
Remove headers like "Connection" and "Keep-Alive" from both upstream and downstream requests. Set to ON by default.
+
+
+
+
+
+
+
+
+
+
HTTP Strict Transport Security
@@ -129,6 +164,7 @@