Enter the username and password for allowing them to access this proxy endpoint
-Username | -Password | -Remove | -
---|---|---|
No Entered Credential | -
diff --git a/src/api.go b/src/api.go index 5831564..86dc35f 100644 --- a/src/api.go +++ b/src/api.go @@ -57,8 +57,8 @@ func initAPIs() { authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener) authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck) //Reverse proxy root related APIs - authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList) - authRouter.HandleFunc("/api/proxy/root/updateOptions", HandleRootRouteOptionsUpdate) + //authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList) + //authRouter.HandleFunc("/api/proxy/root/updateOptions", HandleRootRouteOptionsUpdate) //Reverse proxy auth related APIs authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths) authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths) diff --git a/src/config.go b/src/config.go index c012467..2ab8ab1 100644 --- a/src/config.go +++ b/src/config.go @@ -3,6 +3,7 @@ package main import ( "archive/zip" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -35,97 +36,119 @@ type Record struct { BasicAuthExceptionRules []*dynamicproxy.BasicAuthExceptionRule } -// 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) - - //Generate record - thisRecord := proxyConfigRecord - - //Write to file - js, _ := json.MarshalIndent(thisRecord, "", " ") - return os.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775) -} - -// Save a running reverse proxy endpoint to file (with automatic endpoint to record conversion) -func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) error { - recordToSave, err := ConvertProxyEndpointToRecord(proxyEndpoint) +/* +Load Reverse Proxy Config from file and append it to current runtime proxy router +*/ +func LoadReverseProxyConfig(configFilepath string) error { + //Load the config file from disk + endpointConfig, err := os.ReadFile(configFilepath) if err != nil { return err } - return SaveReverseProxyConfigToFile(recordToSave) -} -func RemoveReverseProxyConfigFile(rootname string) error { - filename := getFilenameFromRootName(rootname) - removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/") - SystemWideLogger.Println("Config Removed: ", removePendingFile) - if utils.FileExists(removePendingFile) { - err := os.Remove(removePendingFile) - if err != nil { - SystemWideLogger.PrintAndLog("Proxy", "Unabel to remove config file", err) - return err - } + //Parse it into dynamic proxy endpoint + thisConfigEndpoint := dynamicproxy.ProxyEndpoint{} + err = json.Unmarshal(endpointConfig, &thisConfigEndpoint) + if err != nil { + return err } - //File already gone + //Matching domain not set. Assume root + if thisConfigEndpoint.RootOrMatchingDomain == "" { + thisConfigEndpoint.RootOrMatchingDomain = "/" + } + + if thisConfigEndpoint.ProxyType == dynamicproxy.ProxyType_Root { + //This is a root config file + rootProxyEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisConfigEndpoint) + if err != nil { + return err + } + + dynamicProxyRouter.SetProxyRouteAsRoot(rootProxyEndpoint) + + } else if thisConfigEndpoint.ProxyType == dynamicproxy.ProxyType_Host { + //This is a host config file + readyProxyEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisConfigEndpoint) + if err != nil { + return err + } + + dynamicProxyRouter.AddProxyRouteToRuntime(readyProxyEndpoint) + } else { + return errors.New("not supported proxy type") + } + + SystemWideLogger.PrintAndLog("Proxy", thisConfigEndpoint.RootOrMatchingDomain+" -> "+thisConfigEndpoint.Domain+" routing rule loaded", nil) return nil } -// Return ptype, rootname and proxyTarget, error if any -func LoadReverseProxyConfig(filename string) (*Record, error) { - thisRecord := Record{ - ProxyType: "", - Rootname: "", - ProxyTarget: "", - UseTLS: false, +func filterProxyConfigFilename(filename string) string { + //Filter out wildcard characters + filename = strings.ReplaceAll(filename, "*", "(ST)") + filename = strings.ReplaceAll(filename, "?", "(QM)") + filename = strings.ReplaceAll(filename, "[", "(OB)") + filename = strings.ReplaceAll(filename, "]", "(CB)") + filename = strings.ReplaceAll(filename, "#", "(HT)") + return filepath.ToSlash(filename) +} + +func SaveReverseProxyConfig(endpoint *dynamicproxy.ProxyEndpoint) error { + //Get filename for saving + filename := filepath.Join("./conf/proxy/", endpoint.RootOrMatchingDomain+".config") + if endpoint.ProxyType == dynamicproxy.ProxyType_Root { + filename = "./conf/proxy/root.config" + } + + filename = filterProxyConfigFilename(filename) + + //Save config to file + js, err := json.MarshalIndent(endpoint, "", " ") + if err != nil { + return err + } + + os.WriteFile(filename, js, 0775) + return nil +} + +func RemoveReverseProxyConfig(endpoint string) error { + filename := filepath.Join("./conf/proxy/", endpoint+".config") + if endpoint == "/" { + filename = "./conf/proxy/root.config" + } + + filename = filterProxyConfigFilename(filename) + + if !utils.FileExists(filename) { + return errors.New("target endpoint not exists") + } + return os.Remove(filename) +} + +// Get the default root config that point to the internal static web server +// this will be used if root config is not found (new deployment / missing root.config file) +func GetDefaultRootConfig() (*dynamicproxy.ProxyEndpoint, error) { + //Default settings + rootProxyEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&dynamicproxy.ProxyEndpoint{ + ProxyType: dynamicproxy.ProxyType_Root, + RootOrMatchingDomain: "/", + Domain: "127.0.0.1:" + staticWebServer.GetListeningPort(), + RequireTLS: false, BypassGlobalTLS: false, - SkipTlsValidation: false, + SkipCertValidations: false, + VirtualDirectories: []*dynamicproxy.ProxyEndpoint{}, RequireBasicAuth: false, BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{}, BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{}, - } - - configContent, err := os.ReadFile(filename) + DefaultSiteOption: dynamicproxy.DefaultSite_InternalStaticWebServer, + DefaultSiteValue: "", + }) if err != nil { - return &thisRecord, err + return nil, err } - //Unmarshal the content into config - err = json.Unmarshal(configContent, &thisRecord) - if err != nil { - return &thisRecord, err - } - - //Return it - 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, - BypassGlobalTLS: targetProxyEndpoint.BypassGlobalTLS, - 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, ".", "_") - filename = strings.ReplaceAll(filename, "/", "-") - filename = filename + ".config" - return filename + return rootProxyEndpoint, nil } /* diff --git a/src/main.go b/src/main.go index ae49a91..94f9988 100644 --- a/src/main.go +++ b/src/main.go @@ -49,9 +49,9 @@ var logOutputToFile = flag.Bool("log", true, "Log terminal output to file") var ( name = "Zoraxy" - version = "2.6.8" + version = "3.0.0" nodeUUID = "generic" - 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/Server.go b/src/mod/dynamicproxy/Server.go index c446277..df69615 100644 --- a/src/mod/dynamicproxy/Server.go +++ b/src/mod/dynamicproxy/Server.go @@ -3,7 +3,6 @@ package dynamicproxy import ( _ "embed" "errors" - "log" "net/http" "net/url" "os" @@ -80,38 +79,26 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } /* - Subdomain Routing + Host Routing */ - if strings.Contains(r.Host, ".") { - //This might be a subdomain. See if there are any subdomain proxy router for this - sep := h.Parent.getSubdomainProxyEndpointFromHostname(domainOnly) - if sep != nil { - if sep.RequireBasicAuth { - err := h.handleBasicAuthRouting(w, r, sep) - if err != nil { - return - } - } - h.subdomainRequest(w, r, sep) - return - } - } - - /* - Virtual Directory Routing - */ - //Clean up the request URI - proxyingPath := strings.TrimSpace(r.RequestURI) - targetProxyEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath) - if targetProxyEndpoint != nil { - if targetProxyEndpoint.RequireBasicAuth { - err := h.handleBasicAuthRouting(w, r, targetProxyEndpoint) + sep := h.Parent.getProxyEndpointFromHostname(domainOnly) + if sep != nil { + if sep.RequireBasicAuth { + err := h.handleBasicAuthRouting(w, r, sep) if err != nil { return } } - h.proxyRequest(w, r, targetProxyEndpoint) - } else if !strings.HasSuffix(proxyingPath, "/") { + h.hostRequest(w, r, sep) + return + } + + /* + Root Router Handling + */ + //Clean up the request URI + proxyingPath := strings.TrimSpace(r.RequestURI) + if !strings.HasSuffix(proxyingPath, "/") { potentialProxtEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath + "/") if potentialProxtEndpoint != nil { //Missing tailing slash. Redirect to target proxy endpoint @@ -136,52 +123,45 @@ Once entered this routing segment, the root routing options will take over for the routing logic. */ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request) { + domainOnly := r.Host if strings.Contains(r.Host, ":") { hostPath := strings.Split(r.Host, ":") domainOnly = hostPath[0] } - if h.Parent.RootRoutingOptions.EnableRedirectForUnsetRules { - //Route to custom domain - if h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget == "" { - //Not set. Redirect to first level of domain redirectable - fld, err := h.getTopLevelRedirectableDomain(domainOnly) - if err != nil { - //Redirect to proxy root - h.proxyRequest(w, r, h.Parent.Root) - } else { - log.Println("[Router] Redirecting request from " + domainOnly + " to " + fld) - h.logRequest(r, false, 307, "root-redirect", domainOnly) - http.Redirect(w, r, fld, http.StatusTemporaryRedirect) - } - return - } else if h.isTopLevelRedirectableDomain(domainOnly) { - //This is requesting a top level private domain that should be serving root - h.proxyRequest(w, r, h.Parent.Root) - } else { - //Validate the redirection target URL - parsedURL, err := url.Parse(h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget) - if err != nil { - //Error when parsing target. Send to root - h.proxyRequest(w, r, h.Parent.Root) - return - } - hostname := parsedURL.Hostname() - if domainOnly != hostname { - //Redirect to target - h.logRequest(r, false, 307, "root-redirect", domainOnly) - http.Redirect(w, r, h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget, http.StatusTemporaryRedirect) - return - } else { - //Loopback request due to bad settings (Shd leave it empty) - //Forward it to root proxy - h.proxyRequest(w, r, h.Parent.Root) - } + //Get the proxy root config + proot := h.Parent.Root + switch proot.DefaultSiteOption { + case DefaultSite_InternalStaticWebServer: + fallthrough + case DefaultSite_ReverseProxy: + //They both share the same behavior + h.vdirRequest(w, r, h.Parent.Root) + case DefaultSite_Redirect: + redirectTarget := strings.TrimSpace(proot.DefaultSiteValue) + if redirectTarget == "" { + redirectTarget = "about:blank" } - } else { - //Route to root - h.proxyRequest(w, r, h.Parent.Root) + + //Check if it is an infinite loopback redirect + parsedURL, err := url.Parse(proot.DefaultSiteValue) + if err != nil { + //Error when parsing target. Send to root + h.vdirRequest(w, r, h.Parent.Root) + return + } + hostname := parsedURL.Hostname() + if hostname == domainOnly { + h.logRequest(r, false, 500, "root-redirect", domainOnly) + http.Error(w, "Loopback redirects due to invalid settings", 500) + return + } + + h.logRequest(r, false, 307, "root-redirect", domainOnly) + http.Redirect(w, r, redirectTarget, http.StatusTemporaryRedirect) + case DefaultSite_NotFoundPage: + http.NotFound(w, r) } } diff --git a/src/mod/dynamicproxy/basicAuth.go b/src/mod/dynamicproxy/basicAuth.go index 8c8b5c3..d4dab2a 100644 --- a/src/mod/dynamicproxy/basicAuth.go +++ b/src/mod/dynamicproxy/basicAuth.go @@ -26,10 +26,6 @@ func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Req } } - proxyType := "vdir-auth" - if pe.ProxyType == ProxyType_Subdomain { - proxyType = "subd-auth" - } u, p, ok := r.BasicAuth() if !ok { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) @@ -48,7 +44,7 @@ func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Req } if !matchingFound { - h.logRequest(r, false, 401, proxyType, pe.Domain) + h.logRequest(r, false, 401, "host", pe.Domain) w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) w.WriteHeader(401) return errors.New("unauthorized") diff --git a/src/mod/dynamicproxy/dynamicproxy.go b/src/mod/dynamicproxy/dynamicproxy.go index d5f5d07..39f4982 100644 --- a/src/mod/dynamicproxy/dynamicproxy.go +++ b/src/mod/dynamicproxy/dynamicproxy.go @@ -22,15 +22,13 @@ import ( func NewDynamicProxy(option RouterOption) (*Router, error) { proxyMap := sync.Map{} - domainMap := sync.Map{} thisRouter := Router{ - Option: &option, - ProxyEndpoints: &proxyMap, - SubdomainEndpoint: &domainMap, - Running: false, - server: nil, - routingRules: []*RoutingRule{}, - tldMap: map[string]int{}, + Option: &option, + ProxyEndpoints: &proxyMap, + Running: false, + server: nil, + routingRules: []*RoutingRule{}, + tldMap: map[string]int{}, } thisRouter.mux = &ProxyHandler{ @@ -84,13 +82,6 @@ func (router *Router) StartProxyService() error { return errors.New("Reverse proxy router root not set") } - //Load root options from file - loadedRootOption, err := loadRootRoutingOptionsFromFile() - if err != nil { - return err - } - router.RootRoutingOptions = loadedRootOption - minVersion := tls.VersionTLS10 if router.Option.ForceTLSLatest { minVersion = tls.VersionTLS12 @@ -129,7 +120,7 @@ func (router *Router) StartProxyService() error { hostPath := strings.Split(r.Host, ":") domainOnly = hostPath[0] } - sep := router.getSubdomainProxyEndpointFromHostname(domainOnly) + sep := router.getProxyEndpointFromHostname(domainOnly) if sep != nil && sep.BypassGlobalTLS { //Allow routing via non-TLS handler originalHostHeader := r.Host @@ -140,7 +131,7 @@ func (router *Router) StartProxyService() error { r.URL, _ = url.Parse(originalHostHeader) } - sep.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ + sep.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ ProxyDomain: sep.Domain, OriginalHost: originalHostHeader, UseTLS: sep.RequireTLS, @@ -280,128 +271,17 @@ func (router *Router) IsProxiedSubdomain(r *http.Request) bool { hostname = r.Host } hostname = strings.Split(hostname, ":")[0] - subdEndpoint := router.getSubdomainProxyEndpointFromHostname(hostname) + subdEndpoint := router.getProxyEndpointFromHostname(hostname) return subdEndpoint != nil } -/* -Add an URL into a custom proxy services -*/ -func (router *Router) AddVirtualDirectoryProxyService(options *VdirOptions) error { - domain := options.Domain - if domain[len(domain)-1:] == "/" { - domain = domain[:len(domain)-1] - } - - /* - if rootname[len(rootname)-1:] == "/" { - rootname = rootname[:len(rootname)-1] - } - */ - - webProxyEndpoint := domain - if options.RequireTLS { - webProxyEndpoint = "https://" + webProxyEndpoint - } else { - webProxyEndpoint = "http://" + webProxyEndpoint - } - //Create a new proxy agent for this root - path, err := url.Parse(webProxyEndpoint) - if err != nil { - return err - } - - 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, - BasicAuthExceptionRules: options.BasicAuthExceptionRules, - Proxy: proxy, - } - - router.ProxyEndpoints.Store(options.RootName, &endpointObject) - - log.Println("Registered Proxy Rule: ", options.RootName+" to "+domain) - return nil -} - /* Load routing from RP */ -func (router *Router) LoadProxy(ptype string, key string) (*ProxyEndpoint, error) { - if ptype == "vdir" { - proxy, ok := router.ProxyEndpoints.Load(key) - if !ok { - return nil, errors.New("target proxy not found") - } - - 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") - } - - targetProxy := proxy.(*ProxyEndpoint) - targetProxy.parent = router - return targetProxy, nil - } - - return nil, errors.New("unsupported ptype") -} - -/* -Add an default router for the proxy server -*/ -func (router *Router) SetRootProxy(options *RootOptions) error { - proxyLocation := options.ProxyLocation - if proxyLocation[len(proxyLocation)-1:] == "/" { - proxyLocation = proxyLocation[:len(proxyLocation)-1] - } - - webProxyEndpoint := proxyLocation - if options.RequireTLS { - webProxyEndpoint = "https://" + webProxyEndpoint - } else { - webProxyEndpoint = "http://" + webProxyEndpoint - } - //Create a new proxy agent for this root - path, err := url.Parse(webProxyEndpoint) - if err != nil { - return err - } - - proxy := dpcore.NewDynamicProxyCore(path, "", options.SkipCertValidations) - - rootEndpoint := ProxyEndpoint{ - ProxyType: ProxyType_Vdir, - RootOrMatchingDomain: "/", - Domain: proxyLocation, - RequireTLS: options.RequireTLS, - SkipCertValidations: options.SkipCertValidations, - RequireBasicAuth: options.RequireBasicAuth, - BasicAuthCredentials: options.BasicAuthCredentials, - BasicAuthExceptionRules: options.BasicAuthExceptionRules, - Proxy: proxy, - } - - router.Root = &rootEndpoint - return nil -} - -// Helpers to export the syncmap for easier processing -func (r *Router) GetSDProxyEndpointsAsMap() map[string]*ProxyEndpoint { - m := make(map[string]*ProxyEndpoint) - r.SubdomainEndpoint.Range(func(key, value interface{}) bool { - k, ok := key.(string) +func (router *Router) LoadProxy(matchingDomain string) (*ProxyEndpoint, error) { + var targetProxyEndpoint *ProxyEndpoint + router.ProxyEndpoints.Range(func(key, value interface{}) bool { + key, ok := key.(string) if !ok { return true } @@ -409,13 +289,32 @@ func (r *Router) GetSDProxyEndpointsAsMap() map[string]*ProxyEndpoint { if !ok { return true } - m[k] = v + + if key == matchingDomain { + targetProxyEndpoint = v + } return true }) - return m + + if targetProxyEndpoint == nil { + return nil, errors.New("target routing rule not found") + } + + return targetProxyEndpoint, nil } -func (r *Router) GetVDProxyEndpointsAsMap() map[string]*ProxyEndpoint { +// Deep copy a proxy endpoint, excluding runtime paramters +func CopyEndpoint(endpoint *ProxyEndpoint) *ProxyEndpoint { + js, _ := json.Marshal(endpoint) + newProxyEndpoint := ProxyEndpoint{} + err := json.Unmarshal(js, &newProxyEndpoint) + if err != nil { + return nil + } + return &newProxyEndpoint +} + +func (r *Router) GetProxyEndpointsAsMap() map[string]*ProxyEndpoint { m := make(map[string]*ProxyEndpoint) r.ProxyEndpoints.Range(func(key, value interface{}) bool { k, ok := key.(string) diff --git a/src/mod/dynamicproxy/endpoints.go b/src/mod/dynamicproxy/endpoints.go new file mode 100644 index 0000000..99c739b --- /dev/null +++ b/src/mod/dynamicproxy/endpoints.go @@ -0,0 +1,65 @@ +package dynamicproxy + +import ( + "errors" + "net/url" + "strings" + + "imuslab.com/zoraxy/mod/dynamicproxy/dpcore" +) + +// Prepare proxy route generate a proxy handler service object for your endpoint +func (router *Router) PrepareProxyRoute(endpoint *ProxyEndpoint) (*ProxyEndpoint, error) { + //Filter the tailing slash if any + domain := endpoint.Domain + if domain[len(domain)-1:] == "/" { + domain = domain[:len(domain)-1] + } + endpoint.Domain = domain + + //Parse the web proxy endpoint + webProxyEndpoint := domain + if !strings.HasPrefix("http://", domain) && !strings.HasPrefix("https://", domain) { + //TLS is not hardcoded in proxy target domain + if endpoint.RequireTLS { + webProxyEndpoint = "https://" + webProxyEndpoint + } else { + webProxyEndpoint = "http://" + webProxyEndpoint + } + } + + //Create a new proxy agent for this root + path, err := url.Parse(webProxyEndpoint) + if err != nil { + return nil, err + } + + //Create the proxy routing handler + proxy := dpcore.NewDynamicProxyCore(path, "", endpoint.SkipCertValidations) + endpoint.proxy = proxy + endpoint.parent = router + + return endpoint, nil +} + +// Add Proxy Route to current runtime. Call to PrepareProxyRoute before adding to runtime +func (router *Router) AddProxyRouteToRuntime(endpoint *ProxyEndpoint) error { + if endpoint.proxy == nil { + //This endpoint is not prepared + return errors.New("proxy endpoint not ready. Use PrepareProxyRoute before adding to runtime") + } + // Push record into running subdomain endpoints + router.ProxyEndpoints.Store(endpoint.RootOrMatchingDomain, endpoint) + return nil +} + +// Set given Proxy Route as Root. Call to PrepareProxyRoute before adding to runtime +func (router *Router) SetProxyRouteAsRoot(endpoint *ProxyEndpoint) error { + if endpoint.proxy == nil { + //This endpoint is not prepared + return errors.New("proxy endpoint not ready. Use PrepareProxyRoute before adding to runtime") + } + // Push record into running root endpoints + router.Root = endpoint + return nil +} diff --git a/src/mod/dynamicproxy/proxyEndpoint.go b/src/mod/dynamicproxy/proxyEndpoint.go index 42a7de1..7e68563 100644 --- a/src/mod/dynamicproxy/proxyEndpoint.go +++ b/src/mod/dynamicproxy/proxyEndpoint.go @@ -1,7 +1,5 @@ package dynamicproxy -import "errors" - /* ProxyEndpoint.go author: tobychui @@ -12,54 +10,20 @@ import "errors" 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 +// 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) - } + ep.parent.ProxyEndpoints.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 +// 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") - + ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain) + return nil } -//ProxyEndpoint remove provide global access by key -func (router *Router) RemoveProxyEndpointByRootname(proxyType string, rootnameOrMatchingDomain string) error { - targetEpt, err := router.LoadProxy(proxyType, rootnameOrMatchingDomain) +// ProxyEndpoint remove provide global access by key +func (router *Router) RemoveProxyEndpointByRootname(rootnameOrMatchingDomain string) error { + targetEpt, err := router.LoadProxy(rootnameOrMatchingDomain) if err != nil { return err } diff --git a/src/mod/dynamicproxy/proxyRequestHandler.go b/src/mod/dynamicproxy/proxyRequestHandler.go index 01d2858..f493cbe 100644 --- a/src/mod/dynamicproxy/proxyRequestHandler.go +++ b/src/mod/dynamicproxy/proxyRequestHandler.go @@ -6,6 +6,7 @@ import ( "net" "net/http" "net/url" + "path/filepath" "strings" "imuslab.com/zoraxy/mod/dynamicproxy/dpcore" @@ -28,13 +29,28 @@ func (router *Router) getTargetProxyEndpointFromRequestURI(requestURI string) *P return targetProxyEndpoint } -func (router *Router) getSubdomainProxyEndpointFromHostname(hostname string) *ProxyEndpoint { +func (router *Router) getProxyEndpointFromHostname(hostname string) *ProxyEndpoint { var targetSubdomainEndpoint *ProxyEndpoint = nil - ep, ok := router.SubdomainEndpoint.Load(hostname) + ep, ok := router.ProxyEndpoints.Load(hostname) if ok { targetSubdomainEndpoint = ep.(*ProxyEndpoint) } + //No hit. Try with wildcard + router.ProxyEndpoints.Range(func(k, v interface{}) bool { + ep := v.(*ProxyEndpoint) + match, err := filepath.Match(ep.RootOrMatchingDomain, hostname) + if err != nil { + //Continue + return true + } + if match { + targetSubdomainEndpoint = ep + return false + } + return true + }) + return targetSubdomainEndpoint } @@ -54,8 +70,8 @@ func (router *Router) rewriteURL(rooturl string, requestURL string) string { return rewrittenURL } -// Handle subdomain request -func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) { +// Handle host request +func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) { r.Header.Set("X-Forwarded-Host", r.Host) r.Header.Set("X-Forwarded-Server", "zoraxy-"+h.Parent.Option.HostUUID) requestURL := r.URL.String() @@ -89,7 +105,7 @@ func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request, r.URL, _ = url.Parse(originalHostHeader) } - err := target.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ + err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ ProxyDomain: target.Domain, OriginalHost: originalHostHeader, UseTLS: target.RequireTLS, @@ -113,7 +129,7 @@ func (h *ProxyHandler) subdomainRequest(w http.ResponseWriter, r *http.Request, } // Handle vdir type request -func (h *ProxyHandler) proxyRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) { +func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, target *ProxyEndpoint) { rewriteURL := h.Parent.rewriteURL(target.RootOrMatchingDomain, r.RequestURI) r.URL, _ = url.Parse(rewriteURL) @@ -144,7 +160,7 @@ func (h *ProxyHandler) proxyRequest(w http.ResponseWriter, r *http.Request, targ r.URL, _ = url.Parse(originalHostHeader) } - err := target.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ + err := target.proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ ProxyDomain: target.Domain, OriginalHost: originalHostHeader, UseTLS: target.RequireTLS, diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index 43c5fa2..ee6a0f8 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -14,8 +14,9 @@ import ( ) const ( - ProxyType_Subdomain = 0 - ProxyType_Vdir = 1 + ProxyType_Root = 0 + ProxyType_Host = 1 + ProxyType_Vdir = 2 ) type ProxyHandler struct { @@ -37,16 +38,14 @@ type RouterOption struct { } type Router struct { - Option *RouterOption - ProxyEndpoints *sync.Map - SubdomainEndpoint *sync.Map - Running bool - Root *ProxyEndpoint - RootRoutingOptions *RootRoutingOptions - mux http.Handler - server *http.Server - tlsListener net.Listener - routingRules []*RoutingRule + Option *RouterOption + ProxyEndpoints *sync.Map + Running bool + Root *ProxyEndpoint + mux http.Handler + server *http.Server + tlsListener net.Listener + routingRules []*RoutingRule tlsRedirectStop chan bool //Stop channel for tls redirection server tldMap map[string]int //Top level domain map, see tld.json @@ -69,63 +68,48 @@ type BasicAuthExceptionRule struct { PathPrefix string } -// A proxy endpoint record +// A proxy endpoint record, a general interface for handling inbound routing type ProxyEndpoint struct { - 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 - BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil) - 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:"-"` + 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 + //TLS/SSL Related + RequireTLS bool //Target domain require TLS + BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil) + SkipCertValidations bool //Set to true to accept self signed certs + + //Virtual Directories + VirtualDirectories []*ProxyEndpoint + + //Authentication + RequireBasicAuth bool //Set to true to request basic auth before proxy + BasicAuthCredentials []*BasicAuthCredentials //Basic auth credentials + BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target + + //Fallback routing logic + DefaultSiteOption int //Fallback routing logic options + DefaultSiteValue string //Fallback routing target, optional + + //Internal Logic Elements parent *Router + proxy *dpcore.ReverseProxy `json:"-"` } +/* + Routing type specific interface + These are options that only avaible for a specific interface + when running, these are converted into "ProxyEndpoint" objects + for more generic routing logic +*/ + // Root options are those that are required for reverse proxy handler to work -type RootOptions struct { - ProxyLocation string //Proxy Root target, all unset traffic will be forward to here - RequireTLS bool //Proxy root target require TLS connection (not recommended) - BypassGlobalTLS bool //Bypass global TLS setting and make root http only (not recommended) - SkipCertValidations bool //Skip cert validation, suitable for self-signed certs, CURRENTLY NOT USED - - //Basic Auth Related - RequireBasicAuth bool //Require basic auth, CURRENTLY NOT USED - BasicAuthCredentials []*BasicAuthCredentials - BasicAuthExceptionRules []*BasicAuthExceptionRule -} - -// Additional options are here for letting router knows how to route exception cases for root -type RootRoutingOptions struct { - //Root only configs - EnableRedirectForUnsetRules bool //Force unset rules to redirect to custom domain - UnsetRuleRedirectTarget string //Custom domain to redirect to for unset rules -} - -type VdirOptions struct { - RootName string - Domain string - RequireTLS bool - BypassGlobalTLS bool - SkipCertValidations bool - RequireBasicAuth bool - BasicAuthCredentials []*BasicAuthCredentials - BasicAuthExceptionRules []*BasicAuthExceptionRule -} - -type SubdOptions struct { - MatchingDomain string - Domain string - RequireTLS bool - BypassGlobalTLS bool - SkipCertValidations bool - RequireBasicAuth bool - BasicAuthCredentials []*BasicAuthCredentials - BasicAuthExceptionRules []*BasicAuthExceptionRule -} +const ( + DefaultSite_InternalStaticWebServer = 0 + DefaultSite_ReverseProxy = 1 + DefaultSite_Redirect = 2 + DefaultSite_NotFoundPage = 3 +) /* Web Templates diff --git a/src/mod/webserv/webserv.go b/src/mod/webserv/webserv.go index 9eb7ee7..d516501 100644 --- a/src/mod/webserv/webserv.go +++ b/src/mod/webserv/webserv.go @@ -89,7 +89,7 @@ func (ws *WebServer) RestorePreviousState() { ws.option.EnableDirectoryListing = enableDirList //Check the running state - webservRunning := false + webservRunning := true ws.option.Sysdb.Read("webserv", "enabled", &webservRunning) if webservRunning { ws.Start() @@ -124,6 +124,11 @@ func (ws *WebServer) ChangePort(port string) error { return nil } +// Get current using port in options +func (ws *WebServer) GetListeningPort() string { + return ws.option.Port +} + // Start starts the web server. func (ws *WebServer) Start() error { ws.mu.Lock() diff --git a/src/reverseproxy.go b/src/reverseproxy.go index f1bd381..c1c56b5 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "net/http" "path/filepath" "sort" @@ -21,6 +22,9 @@ var ( // Add user customizable reverse proxy func ReverseProxtInit() { + /* + Load Reverse Proxy Global Settings + */ inboundPort := 80 if sysdb.KeyExists("settings", "inbound") { sysdb.Read("settings", "inbound", &inboundPort) @@ -63,6 +67,12 @@ func ReverseProxtInit() { SystemWideLogger.Println("Force HTTPS mode disabled") } + /* + Create a new proxy object + The DynamicProxy is the parent of all reverse proxy handlers, + use for managemening and provide functions to access proxy handlers + */ + dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{ HostUUID: nodeUUID, Port: inboundPort, @@ -83,45 +93,28 @@ func ReverseProxtInit() { dynamicProxyRouter = dprouter - //Load all conf from files + /* + + Load all conf from files + + */ confs, _ := filepath.Glob("./conf/proxy/*.config") for _, conf := range confs { - record, err := LoadReverseProxyConfig(conf) + err := LoadReverseProxyConfig(conf) if err != nil { SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err) return } + } - if record.ProxyType == "root" { - dynamicProxyRouter.SetRootProxy(&dynamicproxy.RootOptions{ - ProxyLocation: record.ProxyTarget, - RequireTLS: record.UseTLS, - }) - } else if record.ProxyType == "subd" { - dynamicProxyRouter.AddSubdomainRoutingService(&dynamicproxy.SubdOptions{ - MatchingDomain: record.Rootname, - Domain: record.ProxyTarget, - RequireTLS: record.UseTLS, - BypassGlobalTLS: record.BypassGlobalTLS, - 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, - BypassGlobalTLS: record.BypassGlobalTLS, - SkipCertValidations: record.SkipTlsValidation, - RequireBasicAuth: record.RequireBasicAuth, - BasicAuthCredentials: record.BasicAuthCredentials, - BasicAuthExceptionRules: record.BasicAuthExceptionRules, - }) - } else { - SystemWideLogger.PrintAndLog("Proxy", "Unsupported endpoint type: "+record.ProxyType+". Skipping "+filepath.Base(conf), nil) + if dynamicProxyRouter.Root == nil { + //Root config not set (new deployment?), use internal static web server as root + defaultRootRouter, err := GetDefaultRootConfig() + if err != nil { + SystemWideLogger.PrintAndLog("Proxy", "Failed to generate default root routing", err) + return } + dynamicProxyRouter.SetProxyRouteAsRoot(defaultRootRouter) } //Start Service @@ -173,7 +166,7 @@ func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) { } func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { - eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd + eptype, err := utils.PostPara(r, "type") //Support root and host if err != nil { utils.SendErrorResponse(w, "type not defined") return @@ -241,73 +234,96 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { } } - rootname := "" - if eptype == "vdir" { - vdir, err := utils.PostPara(r, "rootname") - if err != nil { - utils.SendErrorResponse(w, "vdir not defined") - return - } - - //Vdir must start with / - if !strings.HasPrefix(vdir, "/") { - vdir = "/" + vdir - } - rootname = vdir - - thisOption := dynamicproxy.VdirOptions{ - RootName: vdir, - Domain: endpoint, - RequireTLS: useTLS, - BypassGlobalTLS: useBypassGlobalTLS, - SkipCertValidations: skipTlsValidation, - RequireBasicAuth: requireBasicAuth, - BasicAuthCredentials: basicAuthCredentials, - } - dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption) - - } else if eptype == "subd" { - subdomain, err := utils.PostPara(r, "rootname") + var proxyEndpointCreated *dynamicproxy.ProxyEndpoint + if eptype == "host" { + rootOrMatchingDomain, err := utils.PostPara(r, "rootname") if err != nil { utils.SendErrorResponse(w, "subdomain not defined") return } - rootname = subdomain - thisOption := dynamicproxy.SubdOptions{ - MatchingDomain: subdomain, + thisProxyEndpoint := dynamicproxy.ProxyEndpoint{ + //I/O + ProxyType: dynamicproxy.ProxyType_Host, + RootOrMatchingDomain: rootOrMatchingDomain, + Domain: endpoint, + //TLS + RequireTLS: useTLS, + BypassGlobalTLS: useBypassGlobalTLS, + SkipCertValidations: skipTlsValidation, + //VDir + VirtualDirectories: []*dynamicproxy.ProxyEndpoint{}, + //Auth + RequireBasicAuth: requireBasicAuth, + BasicAuthCredentials: basicAuthCredentials, + BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{}, + DefaultSiteOption: 0, + DefaultSiteValue: "", + } + + preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint) + if err != nil { + utils.SendErrorResponse(w, "unable to prepare proxy route to target endpoint: "+err.Error()) + return + } + + dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint) + proxyEndpointCreated = &thisProxyEndpoint + } else if eptype == "root" { + //Get the default site options and target + dsOptString, err := utils.PostPara(r, "defaultSiteOpt") + if err != nil { + utils.SendErrorResponse(w, "default site action not defined") + return + } + + var defaultSiteOption int = 1 + opt, err := strconv.Atoi(dsOptString) + if err != nil { + utils.SendErrorResponse(w, "invalid default site option") + return + } + + defaultSiteOption = opt + + dsVal, err := utils.PostPara(r, "defaultSiteVal") + if err != nil && (defaultSiteOption == 1 || defaultSiteOption == 2) { + //Reverse proxy or redirect, must require value to be set + utils.SendErrorResponse(w, "target not defined") + return + } + + //Write the root options to file + rootRoutingEndpoint := dynamicproxy.ProxyEndpoint{ + ProxyType: dynamicproxy.ProxyType_Root, + RootOrMatchingDomain: "/", Domain: endpoint, RequireTLS: useTLS, - BypassGlobalTLS: useBypassGlobalTLS, - SkipCertValidations: skipTlsValidation, - RequireBasicAuth: requireBasicAuth, - BasicAuthCredentials: basicAuthCredentials, + BypassGlobalTLS: false, + SkipCertValidations: false, + + DefaultSiteOption: defaultSiteOption, + DefaultSiteValue: dsVal, } - dynamicProxyRouter.AddSubdomainRoutingService(&thisOption) - } else if eptype == "root" { - rootname = "root" - thisOption := dynamicproxy.RootOptions{ - ProxyLocation: endpoint, - RequireTLS: useTLS, + preparedRootProxyRoute, err := dynamicProxyRouter.PrepareProxyRoute(&rootRoutingEndpoint) + if err != nil { + utils.SendErrorResponse(w, "unable to prepare root routing: "+err.Error()) + return } - dynamicProxyRouter.SetRootProxy(&thisOption) + + dynamicProxyRouter.SetProxyRouteAsRoot(preparedRootProxyRoute) + proxyEndpointCreated = &rootRoutingEndpoint } else { //Invalid eptype - utils.SendErrorResponse(w, "Invalid endpoint type") + utils.SendErrorResponse(w, "invalid endpoint type") return } - //Save it - thisProxyConfigRecord := Record{ - ProxyType: eptype, - Rootname: rootname, - ProxyTarget: endpoint, - UseTLS: useTLS, - BypassGlobalTLS: useBypassGlobalTLS, - SkipTlsValidation: skipTlsValidation, - RequireBasicAuth: requireBasicAuth, - BasicAuthCredentials: basicAuthCredentials, + //Save the config to file + err = SaveReverseProxyConfig(proxyEndpointCreated) + if err != nil { + SystemWideLogger.PrintAndLog("Proxy", "Unable to save new proxy rule to file", err) + return } - SaveReverseProxyConfigToFile(&thisProxyConfigRecord) //Update utm if exists if uptimeMonitor != nil { @@ -320,17 +336,11 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { /* ReverseProxyHandleEditEndpoint handles proxy endpoint edit -This endpoint do not handle -basic auth credential update. The credential -will be loaded from old config and reused +(host only, for root use Default Site page to edit) +This endpoint do not handle basic auth credential update. +The credential will be loaded from old config and reused */ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { - eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd - if err != nil { - utils.SendErrorResponse(w, "type not defined") - return - } - rootNameOrMatchingDomain, err := utils.PostPara(r, "rootname") if err != nil { utils.SendErrorResponse(w, "Target proxy rule not defined") @@ -371,50 +381,31 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { requireBasicAuth := (rba == "true") //Load the previous basic auth credentials from current proxy rules - targetProxyEntry, err := dynamicProxyRouter.LoadProxy(eptype, rootNameOrMatchingDomain) + targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain) if err != nil { utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded") return } - if eptype == "vdir" { - thisOption := dynamicproxy.VdirOptions{ - RootName: targetProxyEntry.RootOrMatchingDomain, - Domain: endpoint, - RequireTLS: useTLS, - BypassGlobalTLS: false, - SkipCertValidations: skipTlsValidation, - RequireBasicAuth: requireBasicAuth, - BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, - } - targetProxyEntry.Remove() - dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption) + //Generate a new proxyEndpoint from the new config + newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry) + newProxyEndpoint.Domain = endpoint + newProxyEndpoint.RequireTLS = useTLS + newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS + newProxyEndpoint.SkipCertValidations = skipTlsValidation + newProxyEndpoint.RequireBasicAuth = requireBasicAuth - } else if eptype == "subd" { - thisOption := dynamicproxy.SubdOptions{ - MatchingDomain: targetProxyEntry.RootOrMatchingDomain, - Domain: endpoint, - RequireTLS: useTLS, - BypassGlobalTLS: bypassGlobalTLS, - SkipCertValidations: skipTlsValidation, - RequireBasicAuth: requireBasicAuth, - BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, - } - targetProxyEntry.Remove() - dynamicProxyRouter.AddSubdomainRoutingService(&thisOption) + //Prepare to replace the current routing rule + readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return } + targetProxyEntry.Remove() + dynamicProxyRouter.AddProxyRouteToRuntime(readyRoutingRule) //Save it to file - thisProxyConfigRecord := Record{ - ProxyType: eptype, - Rootname: targetProxyEntry.RootOrMatchingDomain, - ProxyTarget: endpoint, - UseTLS: useTLS, - SkipTlsValidation: skipTlsValidation, - RequireBasicAuth: requireBasicAuth, - BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, - } - SaveReverseProxyConfigToFile(&thisProxyConfigRecord) + SaveReverseProxyConfig(newProxyEndpoint) //Update uptime monitor UpdateUptimeMonitorTargets() @@ -429,21 +420,20 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) { return } - ptype, err := utils.PostPara(r, "ptype") - if err != nil { - utils.SendErrorResponse(w, "Invalid ptype given") - return - } - //Remove the config from runtime - err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ptype, ep) + err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ep) if err != nil { utils.SendErrorResponse(w, err.Error()) return } //Remove the config from file - RemoveReverseProxyConfigFile(ep) + fmt.Println(ep) + err = RemoveReverseProxyConfig(ep) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } //Update utm if exists if uptimeMonitor != nil { @@ -473,14 +463,8 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) { 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) + targetProxy, err := dynamicProxyRouter.LoadProxy(ep) if err != nil { utils.SendErrorResponse(w, err.Error()) return @@ -502,17 +486,6 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) { return } - ptype, err := utils.PostPara(r, "ptype") - if err != nil { - utils.SendErrorResponse(w, "Invalid ptype given") - return - } - - if ptype != "vdir" && ptype != "subd" { - utils.SendErrorResponse(w, "Invalid ptype given") - return - } - creds, err := utils.PostPara(r, "creds") if err != nil { utils.SendErrorResponse(w, "Invalid ptype given") @@ -520,7 +493,7 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) { } //Load the target proxy object from router - targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep) + targetProxy, err := dynamicProxyRouter.LoadProxy(ep) if err != nil { utils.SendErrorResponse(w, err.Error()) return @@ -570,7 +543,7 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) { targetProxy.BasicAuthCredentials = mergedCredentials //Save it to file - SaveReverseProxyEndpointToFile(targetProxy) + SaveReverseProxyConfig(targetProxy) //Replace runtime configuration targetProxy.UpdateToRuntime() @@ -593,14 +566,8 @@ func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { 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) + targetProxy, err := dynamicProxyRouter.LoadProxy(ep) if err != nil { utils.SendErrorResponse(w, err.Error()) return @@ -624,12 +591,6 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { 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") @@ -637,7 +598,7 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { } //Load the target proxy object from router - targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep) + targetProxy, err := dynamicProxyRouter.LoadProxy(ep) if err != nil { utils.SendErrorResponse(w, err.Error()) return @@ -666,7 +627,7 @@ func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) { //Save configs to runtime and file targetProxy.UpdateToRuntime() - SaveReverseProxyEndpointToFile(targetProxy) + SaveReverseProxyConfig(targetProxy) utils.SendOK(w) } @@ -679,12 +640,6 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) 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") @@ -692,7 +647,7 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) } // Load the target proxy object from router - targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep) + targetProxy, err := dynamicProxyRouter.LoadProxy(ep) if err != nil { utils.SendErrorResponse(w, err.Error()) return @@ -717,7 +672,7 @@ func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) // Save configs to runtime and file targetProxy.UpdateToRuntime() - SaveReverseProxyEndpointToFile(targetProxy) + SaveReverseProxyConfig(targetProxy) utils.SendOK(w) } @@ -728,16 +683,28 @@ func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) { } func ReverseProxyList(w http.ResponseWriter, r *http.Request) { - eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd + eptype, err := utils.PostPara(r, "type") //Support root and host if err != nil { utils.SendErrorResponse(w, "type not defined") return } - if eptype == "vdir" { + if eptype == "host" { results := []*dynamicproxy.ProxyEndpoint{} dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool { - results = append(results, value.(*dynamicproxy.ProxyEndpoint)) + thisEndpoint := dynamicproxy.CopyEndpoint(value.(*dynamicproxy.ProxyEndpoint)) + + //Clear the auth passwords before showing to front-end + cleanedCredentials := []*dynamicproxy.BasicAuthCredentials{} + for _, user := range thisEndpoint.BasicAuthCredentials { + cleanedCredentials = append(cleanedCredentials, &dynamicproxy.BasicAuthCredentials{ + Username: user.Username, + PasswordHash: "", + }) + } + + thisEndpoint.BasicAuthCredentials = cleanedCredentials + results = append(results, thisEndpoint) return true }) @@ -745,19 +712,6 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) { return results[i].Domain < results[j].Domain }) - js, _ := json.Marshal(results) - utils.SendJSONResponse(w, string(js)) - } else if eptype == "subd" { - results := []*dynamicproxy.ProxyEndpoint{} - dynamicProxyRouter.SubdomainEndpoint.Range(func(key, value interface{}) bool { - results = append(results, value.(*dynamicproxy.ProxyEndpoint)) - return true - }) - - sort.Slice(results, func(i, j int) bool { - return results[i].RootOrMatchingDomain < results[j].RootOrMatchingDomain - }) - js, _ := json.Marshal(results) utils.SendJSONResponse(w, string(js)) } else if eptype == "root" { @@ -881,34 +835,3 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) { utils.SendOK(w) } - -// Handle list of root route options -func HandleRootRouteOptionList(w http.ResponseWriter, r *http.Request) { - js, _ := json.Marshal(dynamicProxyRouter.RootRoutingOptions) - utils.SendJSONResponse(w, string(js)) -} - -// Handle update of the root route edge case options. See dynamicproxy/rootRoute.go -func HandleRootRouteOptionsUpdate(w http.ResponseWriter, r *http.Request) { - enableUnsetSubdomainRedirect, err := utils.PostBool(r, "unsetRedirect") - if err != nil { - utils.SendErrorResponse(w, err.Error()) - return - } - - unsetRedirectTarget, _ := utils.PostPara(r, "unsetRedirectTarget") - - newRootOption := dynamicproxy.RootRoutingOptions{ - EnableRedirectForUnsetRules: enableUnsetSubdomainRedirect, - UnsetRuleRedirectTarget: unsetRedirectTarget, - } - - dynamicProxyRouter.RootRoutingOptions = &newRootOption - err = newRootOption.SaveToFile() - if err != nil { - utils.SendErrorResponse(w, err.Error()) - return - } - - utils.SendOK(w) -} diff --git a/src/start.go b/src/start.go index c7da3b7..7c4be05 100644 --- a/src/start.go +++ b/src/start.go @@ -101,6 +101,18 @@ func startupSequence() { } else { panic(err) } + + //Start the static web server + staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{ + Sysdb: sysdb, + Port: "5487", //Default Port + WebRoot: *staticWebServerRoot, + EnableDirectoryListing: true, + EnableWebDirManager: *allowWebFileManager, + }) + //Restore the web server to previous shutdown state + staticWebServer.RestorePreviousState() + //Create a netstat buffer netstatBuffers, err = netstat.NewNetStatBuffer(300) if err != nil { @@ -220,21 +232,6 @@ func startupSequence() { log.Fatal(err) } - /* - Static Web Server - - Start the static web server - */ - - staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{ - Sysdb: sysdb, - Port: "5487", //Default Port - WebRoot: *staticWebServerRoot, - EnableDirectoryListing: true, - EnableWebDirManager: *allowWebFileManager, - }) - //Restore the web server to previous shutdown state - staticWebServer.RestorePreviousState() } // This sequence start after everything is initialized diff --git a/src/web/components/rproot.html b/src/web/components/rproot.html index a1ed65a..4362aff 100644 --- a/src/web/components/rproot.html +++ b/src/web/components/rproot.html @@ -1,92 +1,119 @@
The default routing point for all incoming traffics. For all routing not found in the proxy rules, request will be redirected to the proxy root server.
+Default routing options for inbound traffic (previously called Proxy Root)
You can create a proxy endpoing by subdomain or virtual directories
-Enter the username and password for allowing them to access this proxy endpoint
-Username | -Password | -Remove | -
---|---|---|
No Entered Credential | -
You can add more proxy rules to support more site via domain / subdomains
+Enter the username and password for allowing them to access this proxy endpoint
+Username | +Password | +Remove | +
---|---|---|
No Entered Credential | +
s1.arozos.com
arozos.com
/s1/home/
/s1/room-
s1.arozos.com
*.arozos.com
/s1/room-101
/s1/room-102/
/s1/room-103/map.txt
www.arozos.com
foo.bar.arozos.com
Overall Network I/O in Current Host Server
+Graph Render Paused
+Graph Render Paused
Inbound Port (Port to be proxied)