diff --git a/src/mod/dynamicproxy/authProviders.go b/src/mod/dynamicproxy/authProviders.go index cfaafa2..127657b 100644 --- a/src/mod/dynamicproxy/authProviders.go +++ b/src/mod/dynamicproxy/authProviders.go @@ -6,6 +6,7 @@ import ( "strings" "imuslab.com/zoraxy/mod/auth" + "imuslab.com/zoraxy/mod/netutils" ) /* @@ -70,9 +71,36 @@ func handleBasicAuth(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) if len(pe.AuthenticationProvider.BasicAuthExceptionRules) > 0 { //Check if the current path matches the exception rules for _, exceptionRule := range pe.AuthenticationProvider.BasicAuthExceptionRules { - if strings.HasPrefix(r.RequestURI, exceptionRule.PathPrefix) { - //This path is excluded from basic auth - return nil + exceptionType := exceptionRule.RuleType + switch exceptionType { + case AuthExceptionType_Paths: + if strings.HasPrefix(r.RequestURI, exceptionRule.PathPrefix) { + //This path is excluded from basic auth + return nil + } + case AuthExceptionType_CIDR: + requesterIp := netutils.GetRequesterIP(r) + if requesterIp != "" { + if requesterIp == exceptionRule.CIDR { + // This IP is excluded from basic auth + return nil + } + + wildcardMatch := netutils.MatchIpWildcard(requesterIp, exceptionRule.CIDR) + if wildcardMatch { + // This IP is excluded from basic auth + return nil + } + + cidrMatch := netutils.MatchIpCIDR(requesterIp, exceptionRule.CIDR) + if cidrMatch { + // This IP is excluded from basic auth + return nil + } + } + default: + //Unknown exception type, skip this rule + continue } } } diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index f6238a3..f285a42 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -106,9 +106,18 @@ type BasicAuthUnhashedCredentials struct { Password string } +type AuthExceptionType int + +const ( + AuthExceptionType_Paths AuthExceptionType = iota //Path exception, match by path prefix + AuthExceptionType_CIDR //CIDR exception, match by CIDR +) + // Paths to exclude in basic auth enabled proxy handler type BasicAuthExceptionRule struct { - PathPrefix string + RuleType AuthExceptionType //The type of the exception rule + PathPrefix string //Path prefix to match, e.g. /api/v1/ + CIDR string //CIDR to match, e.g. 192.168.1.0/24 or IP address, e.g. 192.168.1.1 } /* Routing Rule Data Structures */ diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 61c14e9..67ae40b 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "net" "net/http" "path/filepath" "sort" @@ -956,10 +957,10 @@ 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") @@ -981,6 +982,7 @@ func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { } js, _ := json.Marshal(results) utils.SendJSONResponse(w, string(js)) + return } @@ -991,10 +993,9 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { return } - matchingPrefix, err := utils.PostPara(r, "prefix") + exceptionType, err := utils.PostInt(r, "type") if err != nil { - utils.SendErrorResponse(w, "Invalid matching prefix given") - return + exceptionType = 0x00 //Default to paths } //Load the target proxy object from router @@ -1004,26 +1005,100 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { 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.AuthenticationProvider.BasicAuthExceptionRules { - if thisExceptionRule.PathPrefix == matchingPrefix { - alreadyExists = true - break + switch exceptionType { + case 0x00: + matchingPrefix, err := utils.PostPara(r, "prefix") + if err != nil { + utils.SendErrorResponse(w, "Invalid matching prefix given") + return } - } - if alreadyExists { - utils.SendErrorResponse(w, "This matching path already exists") + + //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.AuthenticationProvider.BasicAuthExceptionRules { + if thisExceptionRule.PathPrefix == matchingPrefix { + alreadyExists = true + break + } + } + if alreadyExists { + utils.SendErrorResponse(w, "This matching path already exists") + return + } + targetProxy.AuthenticationProvider.BasicAuthExceptionRules = append(targetProxy.AuthenticationProvider.BasicAuthExceptionRules, &dynamicproxy.BasicAuthExceptionRule{ + RuleType: dynamicproxy.AuthExceptionType_Paths, + PathPrefix: strings.TrimSpace(matchingPrefix), + }) + + case 0x01: + matchingCIDR, err := utils.PostPara(r, "cidr") + if err != nil { + utils.SendErrorResponse(w, "Invalid matching CIDR given") + return + } + + // Accept CIDR, IP address, or wildcard like 192.168.0.* + matchingCIDR = strings.TrimSpace(matchingCIDR) + isValid := false + + // Check if it's a valid CIDR + if _, _, err := net.ParseCIDR(matchingCIDR); err == nil { + isValid = true + } else if ip := net.ParseIP(matchingCIDR); ip != nil { + // Valid IP address + isValid = true + } else if strings.Contains(matchingCIDR, "*") { + // Accept wildcard like 192.168.0.* + parts := strings.Split(matchingCIDR, ".") + if len(parts) == 4 && parts[3] == "*" { + // Check first 3 parts are numbers 0-255 + validParts := true + for i := 0; i < 3; i++ { + n, err := strconv.Atoi(parts[i]) + if err != nil || n < 0 || n > 255 { + validParts = false + break + } + } + if validParts { + isValid = true + } + } + } + + if !isValid { + utils.SendErrorResponse(w, "Invalid CIDR, IP, or wildcard given") + return + } + + //Add a new exception rule if it is not already exists + alreadyExists := false + for _, thisExceptionRule := range targetProxy.AuthenticationProvider.BasicAuthExceptionRules { + if thisExceptionRule.CIDR == matchingCIDR { + alreadyExists = true + break + } + } + if alreadyExists { + utils.SendErrorResponse(w, "This matching CIDR already exists") + return + } + targetProxy.AuthenticationProvider.BasicAuthExceptionRules = append(targetProxy.AuthenticationProvider.BasicAuthExceptionRules, &dynamicproxy.BasicAuthExceptionRule{ + RuleType: dynamicproxy.AuthExceptionType_CIDR, + CIDR: strings.TrimSpace(matchingCIDR), + }) + + default: + //Invalid exception type given + utils.SendErrorResponse(w, "Invalid exception type given") return + } - targetProxy.AuthenticationProvider.BasicAuthExceptionRules = append(targetProxy.AuthenticationProvider.BasicAuthExceptionRules, &dynamicproxy.BasicAuthExceptionRule{ - PathPrefix: strings.TrimSpace(matchingPrefix), - }) //Save configs to runtime and file targetProxy.UpdateToRuntime() @@ -1040,9 +1115,39 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) return } + exceptionType, err := utils.PostInt(r, "type") + if err != nil { + exceptionType = 0x00 //Default to paths + } + matchingPrefix, err := utils.PostPara(r, "prefix") if err != nil { - utils.SendErrorResponse(w, "Invalid matching prefix given") + matchingPrefix = "" + } + + matchingCIDR, err := utils.PostPara(r, "cidr") + if err != nil { + matchingCIDR = "" + } + + var typeToCheck dynamicproxy.AuthExceptionType + switch exceptionType { + case 0x01: + typeToCheck = dynamicproxy.AuthExceptionType_CIDR + //Check if the CIDR is valid + if matchingCIDR == "" { + utils.SendErrorResponse(w, "Invalid matching CIDR given") + return + } + case 0x00: + fallthrough //For backward compatibility + default: + typeToCheck = dynamicproxy.AuthExceptionType_Paths + //Check if the prefix is valid + if matchingPrefix == "" { + utils.SendErrorResponse(w, "Invalid matching prefix given") + return + } return } @@ -1056,10 +1161,22 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) newExceptionRuleList := []*dynamicproxy.BasicAuthExceptionRule{} matchingExists := false for _, thisExceptionalRule := range targetProxy.AuthenticationProvider.BasicAuthExceptionRules { - if thisExceptionalRule.PathPrefix != matchingPrefix { - newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule) - } else { - matchingExists = true + switch typeToCheck { + case dynamicproxy.AuthExceptionType_CIDR: + if thisExceptionalRule.CIDR != matchingCIDR { + newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule) + } else { + matchingExists = true + } + case dynamicproxy.AuthExceptionType_Paths: + fallthrough //For backward compatibility + default: + if thisExceptionalRule.PathPrefix != matchingPrefix { + newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule) + } else { + matchingExists = true + } + } } diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html index e5fcc27..92544d6 100644 --- a/src/web/components/httprp.html +++ b/src/web/components/httprp.html @@ -1200,6 +1200,9 @@ //Populate all the information in the proxy editor populateAndBindEventsToHTTPProxyEditor(subd); + //Hide all previously opened editor side-frame wrapper + hideEditorSideWrapper(); + //Show the first rpconfig $("#httprpEditModal .rpconfig_content").hide(); $("#httprpEditModal .rpconfig_content[rpcfg='downstream']").show(); diff --git a/src/web/snippet/basicAuthEditor.html b/src/web/snippet/basicAuthEditor.html index 6ac2f0a..01ffd72 100644 --- a/src/web/snippet/basicAuthEditor.html +++ b/src/web/snippet/basicAuthEditor.html @@ -46,24 +46,37 @@
-Exclude specific directories / paths which contains the following subpath prefix from authentication. Useful if you are hosting services require remote API access.
+Exclude specific directories which contains the following subpath prefix or IP / CIDR from authentication. Useful if you are hosting services require remote API access.
Path Prefix | +Exception Type | +Path Prefix / CIDR | Remove | |
---|---|---|---|---|
No Path Excluded | +No Exclusion Rule |