From 4a37a989a01531a79b119f3b538f6310dda6ebc6 Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Sun, 15 Jun 2025 13:46:35 +0800 Subject: [PATCH] Added Disable Chunk Transfer Encoding option - Added disable chunk transfer encoding on UI #685 - Added optional to disable static web server listen to all interface #688 --- src/api.go | 1 + src/mod/dynamicproxy/dpcore/dpcore.go | 7 ++-- src/mod/dynamicproxy/proxyRequestHandler.go | 43 +++++++++++---------- src/mod/dynamicproxy/typedef.go | 3 ++ src/mod/webserv/handler.go | 38 +++++++++++++----- src/mod/webserv/webserv.go | 31 +++++++++++---- src/reverseproxy.go | 4 ++ src/web/components/httprp.html | 43 +++++++++++---------- src/web/components/webserv.html | 31 +++++++++++++++ src/web/snippet/tagEditor.html | 2 +- 10 files changed, 140 insertions(+), 63 deletions(-) diff --git a/src/api.go b/src/api.go index 3371db5..203fa01 100644 --- a/src/api.go +++ b/src/api.go @@ -193,6 +193,7 @@ func RegisterStaticWebServerAPIs(authRouter *auth.RouterDef) { authRouter.HandleFunc("/api/webserv/stop", staticWebServer.HandleStopServer) authRouter.HandleFunc("/api/webserv/setPort", HandleStaticWebServerPortChange) authRouter.HandleFunc("/api/webserv/setDirList", staticWebServer.SetEnableDirectoryListing) + authRouter.HandleFunc("/api/webserv/disableListenAllInterface", staticWebServer.SetDisableListenToAllInterface) /* File Manager */ if *allowWebFileManager { authRouter.HandleFunc("/api/fs/list", staticWebServer.FileManager.HandleList) diff --git a/src/mod/dynamicproxy/dpcore/dpcore.go b/src/mod/dynamicproxy/dpcore/dpcore.go index eb0a489..4566769 100644 --- a/src/mod/dynamicproxy/dpcore/dpcore.go +++ b/src/mod/dynamicproxy/dpcore/dpcore.go @@ -70,8 +70,9 @@ type ResponseRewriteRuleSet struct { DownstreamHeaders [][]string /* Advance Usecase Options */ - HostHeaderOverwrite string //Force overwrite of request "Host" header (advanced usecase) - NoRemoveHopByHop bool //Do not remove hop-by-hop headers (advanced usecase) + HostHeaderOverwrite string //Force overwrite of request "Host" header (advanced usecase) + NoRemoveHopByHop bool //Do not remove hop-by-hop headers (advanced usecase) + DisableChunkedTransferEncoding bool //Disable chunked transfer encoding /* System Information Payload */ Version string //Version number of Zoraxy, use for X-Proxy-By @@ -287,7 +288,7 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr rewriteUserAgent(outreq.Header, "Zoraxy/"+rrr.Version) //Fix proxmox transfer encoding bug if detected Proxmox Cookie - if domainsniff.IsProxmox(req) { + if rrr.DisableChunkedTransferEncoding || domainsniff.IsProxmox(req) { outreq.TransferEncoding = []string{"identity"} } diff --git a/src/mod/dynamicproxy/proxyRequestHandler.go b/src/mod/dynamicproxy/proxyRequestHandler.go index 36e22df..9ba8813 100644 --- a/src/mod/dynamicproxy/proxyRequestHandler.go +++ b/src/mod/dynamicproxy/proxyRequestHandler.go @@ -11,7 +11,6 @@ import ( "sort" "strings" - "imuslab.com/zoraxy/mod/dynamicproxy/domainsniff" "imuslab.com/zoraxy/mod/dynamicproxy/dpcore" "imuslab.com/zoraxy/mod/dynamicproxy/rewrite" "imuslab.com/zoraxy/mod/netutils" @@ -186,16 +185,17 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe //Handle the request reverse proxy statusCode, err := selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ - ProxyDomain: selectedUpstream.OriginIpOrDomain, - OriginalHost: reqHostname, - UseTLS: selectedUpstream.RequireTLS, - NoCache: h.Parent.Option.NoCache, - PathPrefix: "", - UpstreamHeaders: upstreamHeaders, - DownstreamHeaders: downstreamHeaders, - HostHeaderOverwrite: headerRewriteOptions.RequestHostOverwrite, - NoRemoveHopByHop: headerRewriteOptions.DisableHopByHopHeaderRemoval, - Version: target.parent.Option.HostVersion, + ProxyDomain: selectedUpstream.OriginIpOrDomain, + OriginalHost: reqHostname, + UseTLS: selectedUpstream.RequireTLS, + NoCache: h.Parent.Option.NoCache, + PathPrefix: "", + UpstreamHeaders: upstreamHeaders, + DownstreamHeaders: downstreamHeaders, + DisableChunkedTransferEncoding: target.DisableChunkedTransferEncoding, + HostHeaderOverwrite: headerRewriteOptions.RequestHostOverwrite, + NoRemoveHopByHop: headerRewriteOptions.DisableHopByHopHeaderRemoval, + Version: target.parent.Option.HostVersion, }) //validate the error @@ -244,8 +244,8 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe h.Parent.logRequest(r, true, 101, "vdir-websocket", r.Host, target.Domain) wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{ SkipTLSValidation: target.SkipCertValidations, - SkipOriginCheck: target.parent.EnableWebsocketCustomHeaders, //You should not use websocket via virtual directory. But keep this to true for compatibility - CopyAllHeaders: domainsniff.RequireWebsocketHeaderCopy(r), //Left this as default to prevent nginx user setting / as vdir + SkipOriginCheck: true, //You should not use websocket via virtual directory. But keep this to true for compatibility + CopyAllHeaders: target.parent.EnableWebsocketCustomHeaders, //Left this as default to prevent nginx user setting / as vdir UserDefinedHeaders: target.parent.HeaderRewriteRules.UserDefinedHeaders, Logger: h.Parent.Option.Logger, }) @@ -280,14 +280,15 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe //Handle the virtual directory reverse proxy request statusCode, err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ - ProxyDomain: target.Domain, - OriginalHost: reqHostname, - UseTLS: target.RequireTLS, - PathPrefix: target.MatchingPath, - UpstreamHeaders: upstreamHeaders, - DownstreamHeaders: downstreamHeaders, - HostHeaderOverwrite: headerRewriteOptions.RequestHostOverwrite, - Version: target.parent.parent.Option.HostVersion, + ProxyDomain: target.Domain, + OriginalHost: reqHostname, + UseTLS: target.RequireTLS, + PathPrefix: target.MatchingPath, + UpstreamHeaders: upstreamHeaders, + DownstreamHeaders: downstreamHeaders, + DisableChunkedTransferEncoding: target.parent.DisableChunkedTransferEncoding, + HostHeaderOverwrite: headerRewriteOptions.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 36eb39e..7a5b334 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -194,6 +194,9 @@ type ProxyEndpoint struct { //Uptime Monitor DisableUptimeMonitor bool //Disable uptime monitor for this endpoint + // Chunked Transfer Encoding + DisableChunkedTransferEncoding bool //Disable chunked transfer encoding for this endpoint + //Access Control AccessFilterUUID string //Access filter ID diff --git a/src/mod/webserv/handler.go b/src/mod/webserv/handler.go index 74fef4f..e040b1f 100644 --- a/src/mod/webserv/handler.go +++ b/src/mod/webserv/handler.go @@ -17,22 +17,24 @@ import ( */ type StaticWebServerStatus struct { - ListeningPort int - EnableDirectoryListing bool - WebRoot string - Running bool - EnableWebDirManager bool + ListeningPort int + EnableDirectoryListing bool + WebRoot string + Running bool + EnableWebDirManager bool + DisableListenToAllInterface bool } // Handle getting current static web server status func (ws *WebServer) HandleGetStatus(w http.ResponseWriter, r *http.Request) { listeningPortInt, _ := strconv.Atoi(ws.option.Port) currentStatus := StaticWebServerStatus{ - ListeningPort: listeningPortInt, - EnableDirectoryListing: ws.option.EnableDirectoryListing, - WebRoot: ws.option.WebRoot, - Running: ws.isRunning, - EnableWebDirManager: ws.option.EnableWebDirManager, + ListeningPort: listeningPortInt, + EnableDirectoryListing: ws.option.EnableDirectoryListing, + WebRoot: ws.option.WebRoot, + Running: ws.isRunning, + EnableWebDirManager: ws.option.EnableWebDirManager, + DisableListenToAllInterface: ws.option.DisableListenToAllInterface, } js, _ := json.Marshal(currentStatus) @@ -91,3 +93,19 @@ func (ws *WebServer) SetEnableDirectoryListing(w http.ResponseWriter, r *http.Re ws.option.EnableDirectoryListing = enableList utils.SendOK(w) } + +// Get or set disable listen to all interface settings +func (ws *WebServer) SetDisableListenToAllInterface(w http.ResponseWriter, r *http.Request) { + disableListen, err := utils.PostBool(r, "disable") + if err != nil { + utils.SendErrorResponse(w, "invalid setting given") + return + } + err = ws.option.Sysdb.Write("webserv", "disableListenToAllInterface", disableListen) + if err != nil { + utils.SendErrorResponse(w, "unable to save setting") + return + } + ws.option.DisableListenToAllInterface = disableListen + utils.SendOK(w) +} diff --git a/src/mod/webserv/webserv.go b/src/mod/webserv/webserv.go index efb8dc9..85214e4 100644 --- a/src/mod/webserv/webserv.go +++ b/src/mod/webserv/webserv.go @@ -25,13 +25,21 @@ import ( //go:embed templates/* var templates embed.FS +/* +WebServerOptions define the default option for the webserv +might get override by user settings loaded from db + +Any changes in here might need to also update the StaticWebServerStatus struct +in handler.go. See handler.go for more information. +*/ type WebServerOptions struct { - Port string //Port for listening - EnableDirectoryListing bool //Enable listing of directory - WebRoot string //Folder for stroing the static web folders - EnableWebDirManager bool //Enable web file manager to handle files in web directory - Logger *logger.Logger //System logger - Sysdb *database.Database //Database for storing configs + Port string //Port for listening + EnableDirectoryListing bool //Enable listing of directory + WebRoot string //Folder for stroing the static web folders + EnableWebDirManager bool //Enable web file manager to handle files in web directory + DisableListenToAllInterface bool // Disable listening to all interfaces, only listen to localhost + Logger *logger.Logger //System logger + Sysdb *database.Database //Database for storing configs } type WebServer struct { @@ -92,6 +100,11 @@ func (ws *WebServer) RestorePreviousState() { ws.option.Sysdb.Read("webserv", "dirlist", &enableDirList) ws.option.EnableDirectoryListing = enableDirList + //Set disable listen to all interface + disableListenToAll := ws.option.DisableListenToAllInterface + ws.option.Sysdb.Read("webserv", "disableListenToAllInterface", &disableListenToAll) + ws.option.DisableListenToAllInterface = disableListenToAll + //Check the running state webservRunning := true ws.option.Sysdb.Read("webserv", "enabled", &webservRunning) @@ -156,8 +169,12 @@ func (ws *WebServer) Start() error { fs := http.FileServer(http.Dir(filepath.Join(ws.option.WebRoot, "html"))) ws.mux.Handle("/", ws.fsMiddleware(fs)) + listenAddr := ":" + ws.option.Port + if ws.option.DisableListenToAllInterface { + listenAddr = "127.0.0.1:" + ws.option.Port + } ws.server = &http.Server{ - Addr: ":" + ws.option.Port, + Addr: listenAddr, Handler: ws.mux, } diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 88a6a7d..aaccaaf 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -556,6 +556,9 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { proxyRateLimit = 1000 } + // Disable chunked Encoding + disableChunkedEncoding, _ := utils.PostBool(r, "dChunkedEnc") + //Load the previous basic auth credentials from current proxy rules targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain) if err != nil { @@ -596,6 +599,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { newProxyEndpoint.RateLimit = proxyRateLimit newProxyEndpoint.UseStickySession = useStickySession newProxyEndpoint.DisableUptimeMonitor = disbleUtm + newProxyEndpoint.DisableChunkedTransferEncoding = disableChunkedEncoding newProxyEndpoint.Tags = tags //Prepare to replace the current routing rule diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html index 7f71081..1809e1a 100644 --- a/src/web/components/httprp.html +++ b/src/web/components/httprp.html @@ -223,10 +223,10 @@
- +

@@ -288,10 +288,11 @@

- +
-
+ Enabled Upstreams +
@@ -308,17 +309,18 @@ Enable stick session on load balancing

-
+
+ Enable this option if your upstream uses a legacy HTTP server implementation (e.g. Proxmox / opencloud)
-
+ List of Virtual Directories +
@@ -719,6 +721,7 @@ let requireRateLimit = $(editor).find(".RequireRateLimit")[0].checked; let rateLimit = $(editor).find(".RateLimit").val(); let bypassGlobalTLS = $(editor).find(".BypassGlobalTLS")[0].checked; + let disableChunkedTransferEncoding = $(editor).find(".DisableChunkedTransferEncoding")[0].checked; let tags = getTagsArrayFromEndpoint(uuid); if (tags.length > 0){ tags = tags.join(","); @@ -726,7 +729,7 @@ tags = ""; } - console.log({ + cfgPayload = { "type": epttype, "rootname": uuid, "ss":useStickySession, @@ -734,24 +737,16 @@ "bpgtls": bypassGlobalTLS, "authprovider" :authProviderType, "rate" :requireRateLimit, + "dChunkedEnc": disableChunkedTransferEncoding, "ratenum" :rateLimit, "tags": tags, - }); + }; + console.log("updating proxy config:", cfgPayload); $.cjax({ url: "/api/proxy/edit", method: "POST", - data: { - "type": epttype, - "rootname": uuid, - "ss":useStickySession, - "dutm": DisableUptimeMonitor, - "bpgtls": bypassGlobalTLS, - "authprovider" :authProviderType, - "rate" :requireRateLimit, - "ratenum" :rateLimit, - "tags": tags, - }, + data: cfgPayload, success: function(data){ if (data.error !== undefined){ msgbox(data.error, false, 6000); @@ -1143,6 +1138,12 @@ saveProxyInlineEdit(uuid); }); + editor.find(".DisableChunkedTransferEncoding").off("change"); + editor.find(".DisableChunkedTransferEncoding").prop("checked", subd.DisableChunkedTransferEncoding); + editor.find(".DisableChunkedTransferEncoding").on("change", function() { + saveProxyInlineEdit(uuid); + }); + /* ------------ Vdirs ------------ */ editor.find(".vdir_list").html(renderVirtualDirectoryList(subd)); editor.find(".editVdirBtn").off("click").on("click", function(){ diff --git a/src/web/components/webserv.html b/src/web/components/webserv.html index 8d099b6..31e98bd 100644 --- a/src/web/components/webserv.html +++ b/src/web/components/webserv.html @@ -29,6 +29,13 @@ If this folder do not contains any index files, list the directory of this folder.
+
+
+ + + When disabled, the web server will only listen to localhost (127.0.0.1) and only reachable via reverse proxy rules. +
+
@@ -136,6 +143,13 @@ $("#webserv_dirManager").remove(); } + if (!data.DisableListenToAllInterface){ + //Options on UI is flipped + $("#webserv_enableAllInterfaces").parent().checkbox("set checked"); + }else{ + $("#webserv_enableAllInterfaces").parent().checkbox("set unchecked"); + } + $("#webserv_listenPort").val(data.ListeningPort); updateWebServLinkExample(data.ListeningPort); @@ -178,6 +192,23 @@ } }) }); + + $("#webserv_enableAllInterfaces").off("change").on("change", function(){ + let disable = !$(this)[0].checked; + $.cjax({ + url: "/api/webserv/disableListenAllInterface", + method: "POST", + data: {"disable": disable}, + success: function(data){ + if (data.error != undefined){ + msgbox(data.error, false); + }else{ + msgbox("Listening interface setting updated"); + } + } + }) + }); + $("#webserv_listenPort").off("change").on("change", function(){ let newPort = $(this).val(); diff --git a/src/web/snippet/tagEditor.html b/src/web/snippet/tagEditor.html index 25b8a44..d29f266 100644 --- a/src/web/snippet/tagEditor.html +++ b/src/web/snippet/tagEditor.html @@ -22,7 +22,7 @@

Tags currently applied to this host name / proxy rule

-
+