From 4f7f60188fb8a2a72181fb019f2a2950db3198ac Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Tue, 22 Aug 2023 23:46:54 +0800 Subject: [PATCH] Added basic auth exception paths Added feature request from #25 --- src/api.go | 4 + src/config.go | 62 ++++++-- src/main.go | 2 +- src/mod/dynamicproxy/basicAuth.go | 11 ++ src/mod/dynamicproxy/dynamicproxy.go | 55 +++----- src/mod/dynamicproxy/proxyEndpoint.go | 68 +++++++++ src/mod/dynamicproxy/subdomain.go | 15 +- src/mod/dynamicproxy/typedef.go | 77 +++++----- src/reverseproxy.go | 194 ++++++++++++++++++++++---- src/web/components/cert.html | 14 +- src/web/snippet/basicAuthEditor.html | 140 +++++++++++++++---- 11 files changed, 486 insertions(+), 156 deletions(-) create mode 100644 src/mod/dynamicproxy/proxyEndpoint.go diff --git a/src/api.go b/src/api.go index d52b937..673b247 100644 --- a/src/api.go +++ b/src/api.go @@ -55,6 +55,10 @@ func initAPIs() { authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet) authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect) authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck) + //Reverse proxy auth related APIs + authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths) + authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths) + authRouter.HandleFunc("/api/proxy/auth/exceptions/delete", RemoveProxyBasicAuthExceptionPaths) //TLS / SSL config authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy) diff --git a/src/config.go b/src/config.go index 4fce2ef..9150476 100644 --- a/src/config.go +++ b/src/config.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "log" "net/http" "os" @@ -26,16 +25,18 @@ import ( */ type Record struct { - ProxyType string - Rootname string - ProxyTarget string - UseTLS bool - SkipTlsValidation bool - RequireBasicAuth bool - BasicAuthCredentials []*dynamicproxy.BasicAuthCredentials + ProxyType string + Rootname string + ProxyTarget string + UseTLS bool + SkipTlsValidation bool + RequireBasicAuth bool + BasicAuthCredentials []*dynamicproxy.BasicAuthCredentials + BasicAuthExceptionRules []*dynamicproxy.BasicAuthExceptionRule } -func SaveReverseProxyConfig(proxyConfigRecord *Record) error { +// Save a reverse proxy config record to file +func SaveReverseProxyConfigToFile(proxyConfigRecord *Record) error { //TODO: Make this accept new def types os.MkdirAll("./conf/proxy/", 0775) filename := getFilenameFromRootName(proxyConfigRecord.Rootname) @@ -45,10 +46,19 @@ func SaveReverseProxyConfig(proxyConfigRecord *Record) error { //Write to file js, _ := json.MarshalIndent(thisRecord, "", " ") - return ioutil.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775) + return os.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775) } -func RemoveReverseProxyConfig(rootname string) error { +// Save a running reverse proxy endpoint to file (with automatic endpoint to record conversion) +func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) error { + recordToSave, err := ConvertProxyEndpointToRecord(proxyEndpoint) + if err != nil { + return err + } + return SaveReverseProxyConfigToFile(recordToSave) +} + +func RemoveReverseProxyConfigFile(rootname string) error { filename := getFilenameFromRootName(rootname) removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/") log.Println("Config Removed: ", removePendingFile) @@ -66,8 +76,18 @@ func RemoveReverseProxyConfig(rootname string) error { // Return ptype, rootname and proxyTarget, error if any func LoadReverseProxyConfig(filename string) (*Record, error) { - thisRecord := Record{} - configContent, err := ioutil.ReadFile(filename) + thisRecord := Record{ + ProxyType: "", + Rootname: "", + ProxyTarget: "", + UseTLS: false, + SkipTlsValidation: false, + RequireBasicAuth: false, + BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{}, + BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{}, + } + + configContent, err := os.ReadFile(filename) if err != nil { return &thisRecord, err } @@ -82,6 +102,22 @@ func LoadReverseProxyConfig(filename string) (*Record, error) { return &thisRecord, nil } +// Convert a running proxy endpoint object into a save-able record struct +func ConvertProxyEndpointToRecord(targetProxyEndpoint *dynamicproxy.ProxyEndpoint) (*Record, error) { + thisProxyConfigRecord := Record{ + ProxyType: targetProxyEndpoint.GetProxyTypeString(), + Rootname: targetProxyEndpoint.RootOrMatchingDomain, + ProxyTarget: targetProxyEndpoint.Domain, + UseTLS: targetProxyEndpoint.RequireTLS, + SkipTlsValidation: targetProxyEndpoint.SkipCertValidations, + RequireBasicAuth: targetProxyEndpoint.RequireBasicAuth, + BasicAuthCredentials: targetProxyEndpoint.BasicAuthCredentials, + BasicAuthExceptionRules: targetProxyEndpoint.BasicAuthExceptionRules, + } + + return &thisProxyConfigRecord, nil +} + func getFilenameFromRootName(rootname string) string { //Generate a filename for this rootname filename := strings.ReplaceAll(rootname, ".", "_") diff --git a/src/main.go b/src/main.go index 434ce57..d70eb99 100644 --- a/src/main.go +++ b/src/main.go @@ -98,7 +98,7 @@ func ShutdownSeq() { fmt.Println("- Closing Statistic Collector") statisticCollector.Close() if mdnsTickerStop != nil { - fmt.Println("- Stopping mDNS Discoverer") + fmt.Println("- Stopping mDNS Discoverer (might take a few minutes)") // Stop the mdns service mdnsTickerStop <- true } diff --git a/src/mod/dynamicproxy/basicAuth.go b/src/mod/dynamicproxy/basicAuth.go index d08e76a..8c8b5c3 100644 --- a/src/mod/dynamicproxy/basicAuth.go +++ b/src/mod/dynamicproxy/basicAuth.go @@ -3,6 +3,7 @@ package dynamicproxy import ( "errors" "net/http" + "strings" "imuslab.com/zoraxy/mod/auth" ) @@ -15,6 +16,16 @@ import ( */ func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error { + if len(pe.BasicAuthExceptionRules) > 0 { + //Check if the current path matches the exception rules + for _, exceptionRule := range pe.BasicAuthExceptionRules { + if strings.HasPrefix(r.RequestURI, exceptionRule.PathPrefix) { + //This path is excluded from basic auth + return nil + } + } + } + proxyType := "vdir-auth" if pe.ProxyType == ProxyType_Subdomain { proxyType = "subd-auth" diff --git a/src/mod/dynamicproxy/dynamicproxy.go b/src/mod/dynamicproxy/dynamicproxy.go index 8ffe096..c867563 100644 --- a/src/mod/dynamicproxy/dynamicproxy.go +++ b/src/mod/dynamicproxy/dynamicproxy.go @@ -246,14 +246,15 @@ func (router *Router) AddVirtualDirectoryProxyService(options *VdirOptions) erro proxy := dpcore.NewDynamicProxyCore(path, options.RootName, options.SkipCertValidations) endpointObject := ProxyEndpoint{ - ProxyType: ProxyType_Vdir, - RootOrMatchingDomain: options.RootName, - Domain: domain, - RequireTLS: options.RequireTLS, - SkipCertValidations: options.SkipCertValidations, - RequireBasicAuth: options.RequireBasicAuth, - BasicAuthCredentials: options.BasicAuthCredentials, - Proxy: proxy, + ProxyType: ProxyType_Vdir, + RootOrMatchingDomain: options.RootName, + Domain: domain, + RequireTLS: options.RequireTLS, + SkipCertValidations: options.SkipCertValidations, + RequireBasicAuth: options.RequireBasicAuth, + BasicAuthCredentials: options.BasicAuthCredentials, + BasicAuthExceptionRules: options.BasicAuthExceptionRules, + Proxy: proxy, } router.ProxyEndpoints.Store(options.RootName, &endpointObject) @@ -271,46 +272,24 @@ func (router *Router) LoadProxy(ptype string, key string) (*ProxyEndpoint, error if !ok { return nil, errors.New("target proxy not found") } - return proxy.(*ProxyEndpoint), nil + + targetProxy := proxy.(*ProxyEndpoint) + targetProxy.parent = router + return targetProxy, nil } else if ptype == "subd" { proxy, ok := router.SubdomainEndpoint.Load(key) if !ok { return nil, errors.New("target proxy not found") } - return proxy.(*ProxyEndpoint), nil + + targetProxy := proxy.(*ProxyEndpoint) + targetProxy.parent = router + return targetProxy, nil } return nil, errors.New("unsupported ptype") } -/* -Save routing from RP -*/ -func (router *Router) SaveProxy(ptype string, key string, newConfig *ProxyEndpoint) { - if ptype == "vdir" { - router.ProxyEndpoints.Store(key, newConfig) - - } else if ptype == "subd" { - router.SubdomainEndpoint.Store(key, newConfig) - } - -} - -/* -Remove routing from RP -*/ -func (router *Router) RemoveProxy(ptype string, key string) error { - //fmt.Println(ptype, key) - if ptype == "vdir" { - router.ProxyEndpoints.Delete(key) - return nil - } else if ptype == "subd" { - router.SubdomainEndpoint.Delete(key) - return nil - } - return errors.New("invalid ptype") -} - /* Add an default router for the proxy server */ diff --git a/src/mod/dynamicproxy/proxyEndpoint.go b/src/mod/dynamicproxy/proxyEndpoint.go new file mode 100644 index 0000000..42a7de1 --- /dev/null +++ b/src/mod/dynamicproxy/proxyEndpoint.go @@ -0,0 +1,68 @@ +package dynamicproxy + +import "errors" + +/* + ProxyEndpoint.go + author: tobychui + + This script handle the proxy endpoint object actions + so proxyEndpoint can be handled like a proper oop object + + Most of the functions are implemented in dynamicproxy.go +*/ + +//Get the string version of proxy type +func (ep *ProxyEndpoint) GetProxyTypeString() string { + if ep.ProxyType == ProxyType_Subdomain { + return "subd" + } else if ep.ProxyType == ProxyType_Vdir { + return "vdir" + } + + return "unknown" +} + +//Update change in the current running proxy endpoint config +func (ep *ProxyEndpoint) UpdateToRuntime() { + if ep.IsVdir() { + ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep) + + } else if ep.IsSubDomain() { + ep.parent.SubdomainEndpoint.Store(ep.RootOrMatchingDomain, ep) + } +} + +//Return true if the endpoint type is virtual directory +func (ep *ProxyEndpoint) IsVdir() bool { + return ep.ProxyType == ProxyType_Vdir +} + +//Return true if the endpoint type is subdomain +func (ep *ProxyEndpoint) IsSubDomain() bool { + return ep.ProxyType == ProxyType_Subdomain +} + +//Remove this proxy endpoint from running proxy endpoint list +func (ep *ProxyEndpoint) Remove() error { + //fmt.Println(ptype, key) + if ep.IsVdir() { + ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain) + return nil + } else if ep.IsSubDomain() { + ep.parent.SubdomainEndpoint.Delete(ep.RootOrMatchingDomain) + return nil + } + return errors.New("invalid or unsupported type") + +} + +//ProxyEndpoint remove provide global access by key +func (router *Router) RemoveProxyEndpointByRootname(proxyType string, rootnameOrMatchingDomain string) error { + targetEpt, err := router.LoadProxy(proxyType, rootnameOrMatchingDomain) + if err != nil { + return err + } + + return targetEpt.Remove() +} diff --git a/src/mod/dynamicproxy/subdomain.go b/src/mod/dynamicproxy/subdomain.go index 4be9c36..9012724 100644 --- a/src/mod/dynamicproxy/subdomain.go +++ b/src/mod/dynamicproxy/subdomain.go @@ -34,13 +34,14 @@ func (router *Router) AddSubdomainRoutingService(options *SubdOptions) error { proxy := dpcore.NewDynamicProxyCore(path, "", options.SkipCertValidations) router.SubdomainEndpoint.Store(options.MatchingDomain, &ProxyEndpoint{ - RootOrMatchingDomain: options.MatchingDomain, - Domain: domain, - RequireTLS: options.RequireTLS, - Proxy: proxy, - SkipCertValidations: options.SkipCertValidations, - RequireBasicAuth: options.RequireBasicAuth, - BasicAuthCredentials: options.BasicAuthCredentials, + RootOrMatchingDomain: options.MatchingDomain, + Domain: domain, + RequireTLS: options.RequireTLS, + Proxy: proxy, + SkipCertValidations: options.SkipCertValidations, + RequireBasicAuth: options.RequireBasicAuth, + BasicAuthCredentials: options.BasicAuthCredentials, + BasicAuthExceptionRules: options.BasicAuthExceptionRules, }) log.Println("Adding Subdomain Rule: ", options.MatchingDomain+" to "+domain) diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index a865b66..55cd514 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -59,56 +59,51 @@ type BasicAuthUnhashedCredentials struct { Password string } +// Paths to exclude in basic auth enabled proxy handler +type BasicAuthExceptionRule struct { + PathPrefix string +} + // A proxy endpoint record type ProxyEndpoint struct { - ProxyType int //The type of this proxy, see const def - RootOrMatchingDomain string //Root for vdir or Matching domain for subd - Domain string //Domain or IP to proxy to - RequireTLS bool //Target domain require TLS - SkipCertValidations bool //Set to true to accept self signed certs - RequireBasicAuth bool //Set to true to request basic auth before proxy - BasicAuthCredentials []*BasicAuthCredentials `json:"-"` - Proxy *dpcore.ReverseProxy `json:"-"` + ProxyType int //The type of this proxy, see const def + RootOrMatchingDomain string //Root for vdir or Matching domain for subd, also act as key + Domain string //Domain or IP to proxy to + RequireTLS bool //Target domain require TLS + SkipCertValidations bool //Set to true to accept self signed certs + RequireBasicAuth bool //Set to true to request basic auth before proxy + BasicAuthCredentials []*BasicAuthCredentials `json:"-"` //Basic auth credentials + BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target + Proxy *dpcore.ReverseProxy `json:"-"` + + parent *Router } type RootOptions struct { - ProxyLocation string - RequireTLS bool - SkipCertValidations bool - RequireBasicAuth bool - BasicAuthCredentials []*BasicAuthCredentials + ProxyLocation string + RequireTLS bool + SkipCertValidations bool + RequireBasicAuth bool + BasicAuthCredentials []*BasicAuthCredentials + BasicAuthExceptionRules []*BasicAuthExceptionRule } type VdirOptions struct { - RootName string - Domain string - RequireTLS bool - SkipCertValidations bool - RequireBasicAuth bool - BasicAuthCredentials []*BasicAuthCredentials + RootName string + Domain string + RequireTLS bool + SkipCertValidations bool + RequireBasicAuth bool + BasicAuthCredentials []*BasicAuthCredentials + BasicAuthExceptionRules []*BasicAuthExceptionRule } type SubdOptions struct { - MatchingDomain string - Domain string - RequireTLS bool - SkipCertValidations bool - RequireBasicAuth bool - BasicAuthCredentials []*BasicAuthCredentials + MatchingDomain string + Domain string + RequireTLS bool + SkipCertValidations bool + RequireBasicAuth bool + BasicAuthCredentials []*BasicAuthCredentials + BasicAuthExceptionRules []*BasicAuthExceptionRule } - -/* -type ProxyEndpoint struct { - Root string - Domain string - RequireTLS bool - Proxy *reverseproxy.ReverseProxy `json:"-"` -} - -type SubdomainEndpoint struct { - MatchingDomain string - Domain string - RequireTLS bool - Proxy *reverseproxy.ReverseProxy `json:"-"` -} -*/ diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 1e032a4..015016e 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -88,21 +88,23 @@ func ReverseProxtInit() { }) } else if record.ProxyType == "subd" { dynamicProxyRouter.AddSubdomainRoutingService(&dynamicproxy.SubdOptions{ - MatchingDomain: record.Rootname, - Domain: record.ProxyTarget, - RequireTLS: record.UseTLS, - SkipCertValidations: record.SkipTlsValidation, - RequireBasicAuth: record.RequireBasicAuth, - BasicAuthCredentials: record.BasicAuthCredentials, + MatchingDomain: record.Rootname, + Domain: record.ProxyTarget, + RequireTLS: record.UseTLS, + SkipCertValidations: record.SkipTlsValidation, + RequireBasicAuth: record.RequireBasicAuth, + BasicAuthCredentials: record.BasicAuthCredentials, + BasicAuthExceptionRules: record.BasicAuthExceptionRules, }) } else if record.ProxyType == "vdir" { dynamicProxyRouter.AddVirtualDirectoryProxyService(&dynamicproxy.VdirOptions{ - RootName: record.Rootname, - Domain: record.ProxyTarget, - RequireTLS: record.UseTLS, - SkipCertValidations: record.SkipTlsValidation, - RequireBasicAuth: record.RequireBasicAuth, - BasicAuthCredentials: record.BasicAuthCredentials, + RootName: record.Rootname, + Domain: record.ProxyTarget, + RequireTLS: record.UseTLS, + SkipCertValidations: record.SkipTlsValidation, + RequireBasicAuth: record.RequireBasicAuth, + BasicAuthCredentials: record.BasicAuthCredentials, + BasicAuthExceptionRules: record.BasicAuthExceptionRules, }) } else { log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf)) @@ -282,7 +284,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: basicAuthCredentials, } - SaveReverseProxyConfig(&thisProxyConfigRecord) + SaveReverseProxyConfigToFile(&thisProxyConfigRecord) //Update utm if exists if uptimeMonitor != nil { @@ -355,7 +357,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, } - dynamicProxyRouter.RemoveProxy("vdir", thisOption.RootName) + targetProxyEntry.Remove() dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption) } else if eptype == "subd" { @@ -367,7 +369,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, } - dynamicProxyRouter.RemoveProxy("subd", thisOption.MatchingDomain) + targetProxyEntry.Remove() dynamicProxyRouter.AddSubdomainRoutingService(&thisOption) } @@ -381,7 +383,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, } - SaveReverseProxyConfig(&thisProxyConfigRecord) + SaveReverseProxyConfigToFile(&thisProxyConfigRecord) utils.SendOK(w) } @@ -398,13 +400,15 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) { return } - err = dynamicProxyRouter.RemoveProxy(ptype, ep) + //Remove the config from runtime + err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ptype, ep) if err != nil { utils.SendErrorResponse(w, err.Error()) return } - RemoveReverseProxyConfig(ep) + //Remove the config from file + RemoveReverseProxyConfigFile(ep) //Update utm if exists if uptimeMonitor != nil { @@ -528,19 +532,10 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) { targetProxy.BasicAuthCredentials = mergedCredentials //Save it to file - thisProxyConfigRecord := Record{ - ProxyType: ptype, - Rootname: targetProxy.RootOrMatchingDomain, - ProxyTarget: targetProxy.Domain, - UseTLS: targetProxy.RequireTLS, - SkipTlsValidation: targetProxy.SkipCertValidations, - RequireBasicAuth: targetProxy.RequireBasicAuth, - BasicAuthCredentials: targetProxy.BasicAuthCredentials, - } - SaveReverseProxyConfig(&thisProxyConfigRecord) + SaveReverseProxyEndpointToFile(targetProxy) //Replace runtime configuration - dynamicProxyRouter.SaveProxy(ptype, ep, targetProxy) + targetProxy.UpdateToRuntime() utils.SendOK(w) } else { http.Error(w, "invalid usage", http.StatusMethodNotAllowed) @@ -548,6 +543,147 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) { } +// List, Update or Remove the exception paths for basic auth. +func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { + + if r.Method != http.MethodGet { + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + } + ep, err := utils.GetPara(r, "ep") + if err != nil { + utils.SendErrorResponse(w, "Invalid ep given") + return + } + + ptype, err := utils.GetPara(r, "ptype") + if err != nil { + utils.SendErrorResponse(w, "Invalid ptype given") + return + } + + //Load the target proxy object from router + targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + //List all the exception paths for this proxy + results := targetProxy.BasicAuthExceptionRules + if results == nil { + //It is a config from a really old version of zoraxy. Overwrite it with empty array + results = []*dynamicproxy.BasicAuthExceptionRule{} + } + js, _ := json.Marshal(results) + utils.SendJSONResponse(w, string(js)) + return +} + +func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { + ep, err := utils.PostPara(r, "ep") + if err != nil { + utils.SendErrorResponse(w, "Invalid ep given") + return + } + + ptype, err := utils.PostPara(r, "ptype") + if err != nil { + utils.SendErrorResponse(w, "Invalid ptype given") + return + } + + matchingPrefix, err := utils.PostPara(r, "prefix") + if err != nil { + utils.SendErrorResponse(w, "Invalid matching prefix given") + return + } + + //Load the target proxy object from router + targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + //Check if the prefix starts with /. If not, prepend it + if !strings.HasPrefix(matchingPrefix, "/") { + matchingPrefix = "/" + matchingPrefix + } + + //Add a new exception rule if it is not already exists + alreadyExists := false + for _, thisExceptionRule := range targetProxy.BasicAuthExceptionRules { + if thisExceptionRule.PathPrefix == matchingPrefix { + alreadyExists = true + break + } + } + if alreadyExists { + utils.SendErrorResponse(w, "This matching path already exists") + return + } + targetProxy.BasicAuthExceptionRules = append(targetProxy.BasicAuthExceptionRules, &dynamicproxy.BasicAuthExceptionRule{ + PathPrefix: strings.TrimSpace(matchingPrefix), + }) + + //Save configs to runtime and file + targetProxy.UpdateToRuntime() + SaveReverseProxyEndpointToFile(targetProxy) + + utils.SendOK(w) +} + +func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { + // Delete a rule + ep, err := utils.PostPara(r, "ep") + if err != nil { + utils.SendErrorResponse(w, "Invalid ep given") + return + } + + ptype, err := utils.PostPara(r, "ptype") + if err != nil { + utils.SendErrorResponse(w, "Invalid ptype given") + return + } + + matchingPrefix, err := utils.PostPara(r, "prefix") + if err != nil { + utils.SendErrorResponse(w, "Invalid matching prefix given") + return + } + + // Load the target proxy object from router + targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + newExceptionRuleList := []*dynamicproxy.BasicAuthExceptionRule{} + matchingExists := false + for _, thisExceptionalRule := range targetProxy.BasicAuthExceptionRules { + if thisExceptionalRule.PathPrefix != matchingPrefix { + newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule) + } else { + matchingExists = true + } + } + + if !matchingExists { + utils.SendErrorResponse(w, "target matching rule not exists") + return + } + + targetProxy.BasicAuthExceptionRules = newExceptionRuleList + + // Save configs to runtime and file + targetProxy.UpdateToRuntime() + SaveReverseProxyEndpointToFile(targetProxy) + + utils.SendOK(w) +} + func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) { js, _ := json.Marshal(dynamicProxyRouter) utils.SendJSONResponse(w, string(js)) diff --git a/src/web/components/cert.html b/src/web/components/cert.html index fec1014..5f4b75e 100644 --- a/src/web/components/cert.html +++ b/src/web/components/cert.html @@ -1,3 +1,13 @@ +

TLS / SSL Certificates

@@ -111,10 +121,12 @@ return a.Domain > b.Domain }); data.forEach(entry => { + let isExpired = entry.RemainingDays <= 0; + $("#certifiedDomainList").append(` ${entry.Domain} ${entry.LastModifiedDate} - ${entry.ExpireDate} (${entry.RemainingDays} days left) + ${entry.ExpireDate} (${!isExpired?entry.RemainingDays+" days left":"Expired"}) `); }); diff --git a/src/web/snippet/basicAuthEditor.html b/src/web/snippet/basicAuthEditor.html index 4b80546..44ae33d 100644 --- a/src/web/snippet/basicAuthEditor.html +++ b/src/web/snippet/basicAuthEditor.html @@ -42,39 +42,53 @@
+
-
- - -
-

No-Auth Paths

+

Authentication Exclusion Paths

-

Exclude specific paths from the basic auth interface. Useful if you are hosting services require remote API access.

- - - - - - - - - - - - -
UsernamePasswordRemove
No Path Excluded
-
- -
-
- +

Exclude specific directories / paths which contains the following subpath prefix from authentication. Useful if you are hosting services require remote API access.

+ + + + + + + + + + + +
Path PrefixRemove
No Path Excluded
+
+ + Make sure you add the tailing slash for only selecting the files / folder inside that path. +
+
+ +
+
+
+

How to use set excluded paths?

+

All request URI that contains the given prefix will be allowed to bypass authentication and the prefix must start with a slash. For example, given the following prefix.
+ /public/res/
+
+ Zoraxy will allow authentication bypass of any subdirectories or resources under the /public/res/ directory. For example, the following paths access will be able to bypass basic auth mechanism under this setting.
+ /public/res/photo.png
+ /public/res/far/boo/

+
+
+
+ +
+ +