From ce4ce72820271a41c947ec62440e0693e349084d Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Sat, 25 Nov 2023 15:54:28 +0800 Subject: [PATCH] Added optional TLS bypass mechanism + Added opt-out for subdomains for global TLS settings #44 + Optimized subdomain / vdir editing interface --- src/api.go | 1 + src/mod/dynamicproxy/dynamicproxy.go | 56 +++++++++++++++++++++++--- src/mod/dynamicproxy/typedef.go | 1 + src/reverseproxy.go | 50 ++++++++++++++++++++++- src/web/components/rules.html | 59 +++++++++++++++++++--------- src/web/components/status.html | 54 ++++++++++++++++++++++--- src/web/components/subd.html | 14 ++----- src/web/components/vdir.html | 5 ++- 8 files changed, 199 insertions(+), 41 deletions(-) diff --git a/src/api.go b/src/api.go index 5b52342..5831564 100644 --- a/src/api.go +++ b/src/api.go @@ -54,6 +54,7 @@ func initAPIs() { authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS) authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet) authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect) + authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener) authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck) //Reverse proxy root related APIs authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList) diff --git a/src/mod/dynamicproxy/dynamicproxy.go b/src/mod/dynamicproxy/dynamicproxy.go index 4c280ea..d5f5d07 100644 --- a/src/mod/dynamicproxy/dynamicproxy.go +++ b/src/mod/dynamicproxy/dynamicproxy.go @@ -60,6 +60,12 @@ func (router *Router) UpdateTLSVersion(requireLatest bool) { router.Restart() } +// Update port 80 listener state +func (router *Router) UpdatePort80ListenerState(useRedirect bool) { + router.Option.ListenOnPort80 = useRedirect + router.Restart() +} + // Update https redirect, which will require updates func (router *Router) UpdateHttpToHttpsRedirectSetting(useRedirect bool) { router.Option.ForceHttpsRedirect = useRedirect @@ -112,16 +118,56 @@ func (router *Router) StartProxyService() error { } router.Running = true - if router.Option.Port != 80 && router.Option.ForceHttpsRedirect { + if router.Option.Port != 80 && router.Option.ListenOnPort80 { //Add a 80 to 443 redirector httpServer := &http.Server{ Addr: ":80", Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - protocol := "https://" - if router.Option.Port == 443 { - http.Redirect(w, r, protocol+r.Host+r.RequestURI, http.StatusTemporaryRedirect) + //Check if the domain requesting allow non TLS mode + domainOnly := r.Host + if strings.Contains(r.Host, ":") { + hostPath := strings.Split(r.Host, ":") + domainOnly = hostPath[0] + } + sep := router.getSubdomainProxyEndpointFromHostname(domainOnly) + if sep != nil && sep.BypassGlobalTLS { + //Allow routing via non-TLS handler + originalHostHeader := r.Host + if r.URL != nil { + r.Host = r.URL.Host + } else { + //Fallback when the upstream proxy screw something up in the header + r.URL, _ = url.Parse(originalHostHeader) + } + + sep.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ + ProxyDomain: sep.Domain, + OriginalHost: originalHostHeader, + UseTLS: sep.RequireTLS, + PathPrefix: "", + }) + return + } + + if router.Option.ForceHttpsRedirect { + //Redirect to https is enabled + protocol := "https://" + if router.Option.Port == 443 { + http.Redirect(w, r, protocol+r.Host+r.RequestURI, http.StatusTemporaryRedirect) + } else { + http.Redirect(w, r, protocol+r.Host+":"+strconv.Itoa(router.Option.Port)+r.RequestURI, http.StatusTemporaryRedirect) + } } else { - http.Redirect(w, r, protocol+r.Host+":"+strconv.Itoa(router.Option.Port)+r.RequestURI, http.StatusTemporaryRedirect) + //Do not do redirection + if sep != nil { + //Sub-domain exists but not allow non-TLS access + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("400 - Bad Request")) + } else { + //No defined sub-domain + http.NotFound(w, r) + } + } }), diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index 2372f04..43c5fa2 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -27,6 +27,7 @@ type RouterOption struct { Port int //Incoming port UseTls bool //Use TLS to serve incoming requsts ForceTLSLatest bool //Force TLS1.2 or above + ListenOnPort80 bool //Enable port 80 http listener ForceHttpsRedirect bool //Force redirection of http to https endpoint TlsManager *tlscert.Manager RedirectRuleTable *redirection.RuleTable diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 16196a9..f1bd381 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -45,10 +45,20 @@ func ReverseProxtInit() { SystemWideLogger.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0") } + listenOnPort80 := false + sysdb.Read("settings", "listenP80", &listenOnPort80) + if listenOnPort80 { + SystemWideLogger.Println("Port 80 listener enabled") + } else { + SystemWideLogger.Println("Port 80 listener disabled") + } + forceHttpsRedirect := false sysdb.Read("settings", "redirect", &forceHttpsRedirect) if forceHttpsRedirect { SystemWideLogger.Println("Force HTTPS mode enabled") + //Port 80 listener must be enabled to perform http -> https redirect + listenOnPort80 = true } else { SystemWideLogger.Println("Force HTTPS mode disabled") } @@ -58,6 +68,7 @@ func ReverseProxtInit() { Port: inboundPort, UseTls: useTls, ForceTLSLatest: forceLatestTLSVersion, + ListenOnPort80: listenOnPort80, ForceHttpsRedirect: forceHttpsRedirect, TlsManager: tlsCertManager, RedirectRuleTable: redirectTable, @@ -343,9 +354,15 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { if stv == "" { stv = "false" } - skipTlsValidation := (stv == "true") + //Load bypass TLS option + bpgtls, _ := utils.PostPara(r, "bpgtls") + if bpgtls == "" { + bpgtls = "false" + } + bypassGlobalTLS := (bpgtls == "true") + rba, _ := utils.PostPara(r, "bauth") if rba == "" { rba = "false" @@ -365,6 +382,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { RootName: targetProxyEntry.RootOrMatchingDomain, Domain: endpoint, RequireTLS: useTLS, + BypassGlobalTLS: false, SkipCertValidations: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, @@ -377,6 +395,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { MatchingDomain: targetProxyEntry.RootOrMatchingDomain, Domain: endpoint, RequireTLS: useTLS, + BypassGlobalTLS: bypassGlobalTLS, SkipCertValidations: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, @@ -749,6 +768,35 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) { } } +// Handle port 80 incoming traffics +func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) { + enabled, err := utils.GetPara(r, "enable") + if err != nil { + //Load the current status + currentEnabled := false + err = sysdb.Read("settings", "listenP80", ¤tEnabled) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + js, _ := json.Marshal(currentEnabled) + utils.SendJSONResponse(w, string(js)) + } else { + if enabled == "true" { + sysdb.Write("settings", "listenP80", true) + SystemWideLogger.Println("Enabling port 80 listener") + dynamicProxyRouter.UpdatePort80ListenerState(true) + } else if enabled == "false" { + sysdb.Write("settings", "listenP80", false) + SystemWideLogger.Println("Disabling port 80 listener") + dynamicProxyRouter.UpdatePort80ListenerState(true) + } else { + utils.SendErrorResponse(w, "invalid mode given: "+enabled) + } + utils.SendOK(w) + } +} + // Handle https redirect func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) { useRedirect, err := utils.GetPara(r, "set") diff --git a/src/web/components/rules.html b/src/web/components/rules.html index 4e9bfb3..81f8a81 100644 --- a/src/web/components/rules.html +++ b/src/web/components/rules.html @@ -8,7 +8,7 @@