12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172 |
- package main
- import (
- "encoding/json"
- "net/http"
- "path/filepath"
- "sort"
- "strconv"
- "strings"
- "time"
- "imuslab.com/zoraxy/mod/auth"
- "imuslab.com/zoraxy/mod/dynamicproxy"
- "imuslab.com/zoraxy/mod/uptime"
- "imuslab.com/zoraxy/mod/utils"
- )
- var (
- dynamicProxyRouter *dynamicproxy.Router
- )
- // Add user customizable reverse proxy
- func ReverseProxtInit() {
- /*
- Load Reverse Proxy Global Settings
- */
- inboundPort := 80
- if sysdb.KeyExists("settings", "inbound") {
- sysdb.Read("settings", "inbound", &inboundPort)
- SystemWideLogger.Println("Serving inbound port ", inboundPort)
- } else {
- SystemWideLogger.Println("Inbound port not set. Using default (80)")
- }
- useTls := false
- sysdb.Read("settings", "usetls", &useTls)
- if useTls {
- SystemWideLogger.Println("TLS mode enabled. Serving proxxy request with TLS")
- } else {
- SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http")
- }
- forceLatestTLSVersion := false
- sysdb.Read("settings", "forceLatestTLS", &forceLatestTLSVersion)
- if forceLatestTLSVersion {
- SystemWideLogger.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2")
- } else {
- SystemWideLogger.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0")
- }
- developmentMode := false
- sysdb.Read("settings", "devMode", &developmentMode)
- if useTls {
- SystemWideLogger.Println("Development mode enabled. Using no-store Cache Control policy")
- } else {
- SystemWideLogger.Println("Development mode disabled. Proxying with default Cache Control policy")
- }
- 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")
- }
- /*
- 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,
- HostVersion: version,
- Port: inboundPort,
- UseTls: useTls,
- ForceTLSLatest: forceLatestTLSVersion,
- NoCache: developmentMode,
- ListenOnPort80: listenOnPort80,
- ForceHttpsRedirect: forceHttpsRedirect,
- TlsManager: tlsCertManager,
- RedirectRuleTable: redirectTable,
- GeodbStore: geodbStore,
- StatisticCollector: statisticCollector,
- WebDirectory: *staticWebServerRoot,
- AccessController: accessController,
- })
- if err != nil {
- SystemWideLogger.PrintAndLog("Proxy", "Unable to create dynamic proxy router", err)
- return
- }
- dynamicProxyRouter = dprouter
- /*
- Load all conf from files
- */
- confs, _ := filepath.Glob("./conf/proxy/*.config")
- for _, conf := range confs {
- err := LoadReverseProxyConfig(conf)
- if err != nil {
- SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err)
- return
- }
- }
- 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
- //Not sure why but delay must be added if you have another
- //reverse proxy server in front of this service
- time.Sleep(300 * time.Millisecond)
- dynamicProxyRouter.StartProxyService()
- SystemWideLogger.Println("Dynamic Reverse Proxy service started")
- //Add all proxy services to uptime monitor
- //Create a uptime monitor service
- go func() {
- //This must be done in go routine to prevent blocking on system startup
- uptimeMonitor, _ = uptime.NewUptimeMonitor(&uptime.Config{
- Targets: GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter),
- Interval: 300, //5 minutes
- MaxRecordsStore: 288, //1 day
- })
- SystemWideLogger.Println("Uptime Monitor background service started")
- }()
- }
- func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) {
- enable, _ := utils.PostPara(r, "enable") //Support root, vdir and subd
- if enable == "true" {
- err := dynamicProxyRouter.StartProxyService()
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- } else {
- //Check if it is loopback
- if dynamicProxyRouter.IsProxiedSubdomain(r) {
- //Loopback routing. Turning it off will make the user lost control
- //of the whole system. Do not allow shutdown
- utils.SendErrorResponse(w, "Unable to shutdown in loopback rp mode. Remove proxy rules for management interface and retry.")
- return
- }
- err := dynamicProxyRouter.StopProxyService()
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- }
- utils.SendOK(w)
- }
- func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
- eptype, err := utils.PostPara(r, "type") //Support root and host
- if err != nil {
- utils.SendErrorResponse(w, "type not defined")
- return
- }
- endpoint, err := utils.PostPara(r, "ep")
- if err != nil {
- utils.SendErrorResponse(w, "endpoint not defined")
- return
- }
- tls, _ := utils.PostPara(r, "tls")
- if tls == "" {
- tls = "false"
- }
- useTLS := (tls == "true")
- //Bypass global TLS value / allow direct access from port 80?
- bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS")
- if bypassGlobalTLS == "" {
- bypassGlobalTLS = "false"
- }
- useBypassGlobalTLS := bypassGlobalTLS == "true"
- //Enable TLS validation?
- stv, _ := utils.PostPara(r, "tlsval")
- if stv == "" {
- stv = "false"
- }
- skipTlsValidation := (stv == "true")
- //Get access rule ID
- accessRuleID, _ := utils.PostPara(r, "access")
- if accessRuleID == "" {
- accessRuleID = "default"
- }
- if !accessController.AccessRuleExists(accessRuleID) {
- utils.SendErrorResponse(w, "invalid access rule ID selected")
- return
- }
- //Require basic auth?
- rba, _ := utils.PostPara(r, "bauth")
- if rba == "" {
- rba = "false"
- }
- requireBasicAuth := (rba == "true")
- // Bypass WebSocket Origin Check
- strbpwsorg, _ := utils.PostPara(r, "bpwsorg")
- if strbpwsorg == "" {
- strbpwsorg = "false"
- }
- bypassWebsocketOriginCheck := (strbpwsorg == "true")
- //Prase the basic auth to correct structure
- cred, _ := utils.PostPara(r, "cred")
- basicAuthCredentials := []*dynamicproxy.BasicAuthCredentials{}
- if requireBasicAuth {
- preProcessCredentials := []*dynamicproxy.BasicAuthUnhashedCredentials{}
- err = json.Unmarshal([]byte(cred), &preProcessCredentials)
- if err != nil {
- utils.SendErrorResponse(w, "invalid user credentials")
- return
- }
- //Check if there are empty password credentials
- for _, credObj := range preProcessCredentials {
- if strings.TrimSpace(credObj.Password) == "" {
- utils.SendErrorResponse(w, credObj.Username+" has empty password")
- return
- }
- }
- //Convert and hash the passwords
- for _, credObj := range preProcessCredentials {
- basicAuthCredentials = append(basicAuthCredentials, &dynamicproxy.BasicAuthCredentials{
- Username: credObj.Username,
- PasswordHash: auth.Hash(credObj.Password),
- })
- }
- }
- var proxyEndpointCreated *dynamicproxy.ProxyEndpoint
- if eptype == "host" {
- rootOrMatchingDomain, err := utils.PostPara(r, "rootname")
- if err != nil {
- utils.SendErrorResponse(w, "hostname not defined")
- return
- }
- rootOrMatchingDomain = strings.TrimSpace(rootOrMatchingDomain)
- //Check if it contains ",", if yes, split the remainings as alias
- aliasHostnames := []string{}
- if strings.Contains(rootOrMatchingDomain, ",") {
- matchingDomains := strings.Split(rootOrMatchingDomain, ",")
- if len(matchingDomains) > 1 {
- rootOrMatchingDomain = matchingDomains[0]
- for _, aliasHostname := range matchingDomains[1:] {
- //Filter out any space
- aliasHostnames = append(aliasHostnames, strings.TrimSpace(aliasHostname))
- }
- }
- }
- //Generate a proxy endpoint object
- thisProxyEndpoint := dynamicproxy.ProxyEndpoint{
- //I/O
- ProxyType: dynamicproxy.ProxyType_Host,
- RootOrMatchingDomain: rootOrMatchingDomain,
- MatchingDomainAlias: aliasHostnames,
- Domain: endpoint,
- //TLS
- RequireTLS: useTLS,
- BypassGlobalTLS: useBypassGlobalTLS,
- SkipCertValidations: skipTlsValidation,
- SkipWebSocketOriginCheck: bypassWebsocketOriginCheck,
- AccessFilterUUID: accessRuleID,
- //VDir
- VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
- //Custom headers
- UserDefinedHeaders: []*dynamicproxy.UserDefinedHeader{},
- //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: false,
- SkipCertValidations: false,
- SkipWebSocketOriginCheck: true,
- DefaultSiteOption: defaultSiteOption,
- DefaultSiteValue: dsVal,
- }
- preparedRootProxyRoute, err := dynamicProxyRouter.PrepareProxyRoute(&rootRoutingEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, "unable to prepare root routing: "+err.Error())
- return
- }
- dynamicProxyRouter.SetProxyRouteAsRoot(preparedRootProxyRoute)
- proxyEndpointCreated = &rootRoutingEndpoint
- } else {
- //Invalid eptype
- utils.SendErrorResponse(w, "invalid endpoint type")
- return
- }
- //Save the config to file
- err = SaveReverseProxyConfig(proxyEndpointCreated)
- if err != nil {
- SystemWideLogger.PrintAndLog("Proxy", "Unable to save new proxy rule to file", err)
- return
- }
- //Update utm if exists
- UpdateUptimeMonitorTargets()
- utils.SendOK(w)
- }
- /*
- ReverseProxyHandleEditEndpoint handles proxy endpoint edit
- (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) {
- rootNameOrMatchingDomain, err := utils.PostPara(r, "rootname")
- if err != nil {
- utils.SendErrorResponse(w, "Target proxy rule not defined")
- return
- }
- endpoint, err := utils.PostPara(r, "ep")
- if err != nil {
- utils.SendErrorResponse(w, "endpoint not defined")
- return
- }
- tls, _ := utils.PostPara(r, "tls")
- if tls == "" {
- tls = "false"
- }
- useTLS := (tls == "true")
- stv, _ := utils.PostPara(r, "tlsval")
- if stv == "" {
- stv = "false"
- }
- skipTlsValidation := (stv == "true")
- //Load bypass TLS option
- bpgtls, _ := utils.PostPara(r, "bpgtls")
- if bpgtls == "" {
- bpgtls = "false"
- }
- bypassGlobalTLS := (bpgtls == "true")
- // Basic Auth
- rba, _ := utils.PostPara(r, "bauth")
- if rba == "" {
- rba = "false"
- }
- requireBasicAuth := (rba == "true")
- // Bypass WebSocket Origin Check
- strbpwsorg, _ := utils.PostPara(r, "bpwsorg")
- if strbpwsorg == "" {
- strbpwsorg = "false"
- }
- bypassWebsocketOriginCheck := (strbpwsorg == "true")
- //Load the previous basic auth credentials from current proxy rules
- targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
- if err != nil {
- utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
- return
- }
- //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
- newProxyEndpoint.SkipWebSocketOriginCheck = bypassWebsocketOriginCheck
- //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
- SaveReverseProxyConfig(newProxyEndpoint)
- //Update uptime monitor
- UpdateUptimeMonitorTargets()
- utils.SendOK(w)
- }
- func ReverseProxyHandleAlias(w http.ResponseWriter, r *http.Request) {
- rootNameOrMatchingDomain, err := utils.PostPara(r, "ep")
- if err != nil {
- utils.SendErrorResponse(w, "Invalid ep given")
- return
- }
- //No need to check for type as root (/) can be set to default route
- //and hence, you will not need alias
- //Load the previous alias from current proxy rules
- targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
- if err != nil {
- utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
- return
- }
- newAliasJSON, err := utils.PostPara(r, "alias")
- if err != nil {
- //No new set of alias given
- utils.SendErrorResponse(w, "new alias not given")
- return
- }
- //Write new alias to runtime and file
- newAlias := []string{}
- err = json.Unmarshal([]byte(newAliasJSON), &newAlias)
- if err != nil {
- SystemWideLogger.PrintAndLog("Proxy", "Unable to parse new alias list", err)
- utils.SendErrorResponse(w, "Invalid alias list given")
- return
- }
- //Set the current alias
- newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
- newProxyEndpoint.MatchingDomainAlias = newAlias
- // 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
- err = SaveReverseProxyConfig(newProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, "Alias update failed")
- SystemWideLogger.PrintAndLog("Proxy", "Unable to save alias update", err)
- }
- utils.SendOK(w)
- }
- func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
- ep, err := utils.GetPara(r, "ep")
- if err != nil {
- utils.SendErrorResponse(w, "Invalid ep given")
- return
- }
- //Remove the config from runtime
- err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ep)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Remove the config from file
- err = RemoveReverseProxyConfig(ep)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Update utm if exists
- if uptimeMonitor != nil {
- uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
- uptimeMonitor.CleanRecords()
- }
- //Update uptime monitor
- UpdateUptimeMonitorTargets()
- utils.SendOK(w)
- }
- /*
- Handle update request for basic auth credential
- Require paramter: ep (Endpoint) and pytype (proxy Type)
- if request with GET, the handler will return current credentials
- on this endpoint by its username
- if request is POST, the handler will write the results to proxy config
- */
- func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
- if r.Method == http.MethodGet {
- ep, err := utils.GetPara(r, "ep")
- if err != nil {
- utils.SendErrorResponse(w, "Invalid ep given")
- return
- }
- //Load the target proxy object from router
- targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- usernames := []string{}
- for _, cred := range targetProxy.BasicAuthCredentials {
- usernames = append(usernames, cred.Username)
- }
- js, _ := json.Marshal(usernames)
- utils.SendJSONResponse(w, string(js))
- } else if r.Method == http.MethodPost {
- //Write to target
- ep, err := utils.PostPara(r, "ep")
- if err != nil {
- utils.SendErrorResponse(w, "Invalid ep given")
- return
- }
- creds, err := utils.PostPara(r, "creds")
- if err != nil {
- utils.SendErrorResponse(w, "Invalid ptype given")
- return
- }
- //Load the target proxy object from router
- targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Try to marshal the content of creds into the suitable structure
- newCredentials := []*dynamicproxy.BasicAuthUnhashedCredentials{}
- err = json.Unmarshal([]byte(creds), &newCredentials)
- if err != nil {
- utils.SendErrorResponse(w, "Malformed credential data")
- return
- }
- //Merge the credentials into the original config
- //If a new username exists in old config with no pw given, keep the old pw hash
- //If a new username is found with new password, hash it and push to credential slice
- mergedCredentials := []*dynamicproxy.BasicAuthCredentials{}
- for _, credential := range newCredentials {
- if credential.Password == "" {
- //Check if exists in the old credential files
- keepUnchange := false
- for _, oldCredEntry := range targetProxy.BasicAuthCredentials {
- if oldCredEntry.Username == credential.Username {
- //Exists! Reuse the old hash
- mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
- Username: oldCredEntry.Username,
- PasswordHash: oldCredEntry.PasswordHash,
- })
- keepUnchange = true
- }
- }
- if !keepUnchange {
- //This is a new username with no pw given
- utils.SendErrorResponse(w, "Access password for "+credential.Username+" is empty!")
- return
- }
- } else {
- //This username have given password
- mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
- Username: credential.Username,
- PasswordHash: auth.Hash(credential.Password),
- })
- }
- }
- targetProxy.BasicAuthCredentials = mergedCredentials
- //Save it to file
- SaveReverseProxyConfig(targetProxy)
- //Replace runtime configuration
- targetProxy.UpdateToRuntime()
- utils.SendOK(w)
- } else {
- http.Error(w, "invalid usage", http.StatusMethodNotAllowed)
- }
- }
- // 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
- }
- //Load the target proxy object from router
- targetProxy, err := dynamicProxyRouter.LoadProxy(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
- }
- 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(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()
- SaveReverseProxyConfig(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
- }
- 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(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()
- SaveReverseProxyConfig(targetProxy)
- utils.SendOK(w)
- }
- // Report the current status of the reverse proxy server
- func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
- js, _ := json.Marshal(dynamicProxyRouter)
- utils.SendJSONResponse(w, string(js))
- }
- // Toggle a certain rule on and off
- func ReverseProxyToggleRuleSet(w http.ResponseWriter, r *http.Request) {
- //No need to check for type as root cannot be turned off
- ep, err := utils.PostPara(r, "ep")
- if err != nil {
- utils.SendErrorResponse(w, "invalid ep given")
- return
- }
- targetProxyRule, err := dynamicProxyRouter.LoadProxy(ep)
- if err != nil {
- utils.SendErrorResponse(w, "invalid endpoint given")
- return
- }
- enableStr, err := utils.PostPara(r, "enable")
- if err != nil {
- enableStr = "true"
- }
- //Flip the enable and disabled tag state
- ruleDisabled := enableStr == "false"
- targetProxyRule.Disabled = ruleDisabled
- err = SaveReverseProxyConfig(targetProxyRule)
- if err != nil {
- utils.SendErrorResponse(w, "unable to save updated rule")
- return
- }
- utils.SendOK(w)
- }
- func ReverseProxyListDetail(w http.ResponseWriter, r *http.Request) {
- eptype, err := utils.PostPara(r, "type") //Support root and host
- if err != nil {
- utils.SendErrorResponse(w, "type not defined")
- return
- }
- if eptype == "host" {
- epname, err := utils.PostPara(r, "epname")
- if err != nil {
- utils.SendErrorResponse(w, "epname not defined")
- return
- }
- endpointRaw, ok := dynamicProxyRouter.ProxyEndpoints.Load(epname)
- if !ok {
- utils.SendErrorResponse(w, "proxy rule not found")
- return
- }
- targetEndpoint := dynamicproxy.CopyEndpoint(endpointRaw.(*dynamicproxy.ProxyEndpoint))
- js, _ := json.Marshal(targetEndpoint)
- utils.SendJSONResponse(w, string(js))
- } else if eptype == "root" {
- js, _ := json.Marshal(dynamicProxyRouter.Root)
- utils.SendJSONResponse(w, string(js))
- } else {
- utils.SendErrorResponse(w, "Invalid type given")
- }
- }
- func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
- eptype, err := utils.PostPara(r, "type") //Support root and host
- if err != nil {
- utils.SendErrorResponse(w, "type not defined")
- return
- }
- if eptype == "host" {
- results := []*dynamicproxy.ProxyEndpoint{}
- dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
- 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
- })
- sort.Slice(results, func(i, j int) bool {
- return results[i].Domain < results[j].Domain
- })
- js, _ := json.Marshal(results)
- utils.SendJSONResponse(w, string(js))
- } else if eptype == "root" {
- js, _ := json.Marshal(dynamicProxyRouter.Root)
- utils.SendJSONResponse(w, string(js))
- } else {
- utils.SendErrorResponse(w, "Invalid type given")
- }
- }
- // 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(false)
- } 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")
- if err != nil {
- currentRedirectToHttps := false
- //Load the current status
- err = sysdb.Read("settings", "redirect", ¤tRedirectToHttps)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- js, _ := json.Marshal(currentRedirectToHttps)
- utils.SendJSONResponse(w, string(js))
- } else {
- if dynamicProxyRouter.Option.Port == 80 {
- utils.SendErrorResponse(w, "This option is not available when listening on port 80")
- return
- }
- if useRedirect == "true" {
- sysdb.Write("settings", "redirect", true)
- SystemWideLogger.Println("Updating force HTTPS redirection to true")
- dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
- } else if useRedirect == "false" {
- sysdb.Write("settings", "redirect", false)
- SystemWideLogger.Println("Updating force HTTPS redirection to false")
- dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
- }
- utils.SendOK(w)
- }
- }
- // Handle checking if the current user is accessing via the reverse proxied interface
- // Of the management interface.
- func HandleManagementProxyCheck(w http.ResponseWriter, r *http.Request) {
- isProxied := dynamicProxyRouter.IsProxiedSubdomain(r)
- js, _ := json.Marshal(isProxied)
- utils.SendJSONResponse(w, string(js))
- }
- func HandleDevelopmentModeChange(w http.ResponseWriter, r *http.Request) {
- enableDevelopmentModeStr, err := utils.GetPara(r, "enable")
- if err != nil {
- //Load the current development mode toggle state
- js, _ := json.Marshal(dynamicProxyRouter.Option.NoCache)
- utils.SendJSONResponse(w, string(js))
- } else {
- //Write changes to runtime
- enableDevelopmentMode := false
- if enableDevelopmentModeStr == "true" {
- enableDevelopmentMode = true
- }
- //Write changes to runtime
- dynamicProxyRouter.Option.NoCache = enableDevelopmentMode
- //Write changes to database
- sysdb.Write("settings", "devMode", enableDevelopmentMode)
- utils.SendOK(w)
- }
- }
- // Handle incoming port set. Change the current proxy incoming port
- func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
- newIncomingPort, err := utils.PostPara(r, "incoming")
- if err != nil {
- utils.SendErrorResponse(w, "invalid incoming port given")
- return
- }
- newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
- if err != nil {
- utils.SendErrorResponse(w, "Invalid incoming port given")
- return
- }
- //Check if it is identical as proxy root (recursion!)
- if dynamicProxyRouter.Root == nil || dynamicProxyRouter.Root.Domain == "" {
- //Check if proxy root is set before checking recursive listen
- //Fixing issue #43
- utils.SendErrorResponse(w, "Set Proxy Root before changing inbound port")
- return
- }
- proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/")
- if strings.EqualFold(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.EqualFold(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) {
- //Listening port is same as proxy root
- //Not allow recursive settings
- utils.SendErrorResponse(w, "Recursive listening port! Check your proxy root settings.")
- return
- }
- //Stop and change the setting of the reverse proxy service
- if dynamicProxyRouter.Running {
- dynamicProxyRouter.StopProxyService()
- dynamicProxyRouter.Option.Port = newIncomingPortInt
- dynamicProxyRouter.StartProxyService()
- } else {
- //Only change setting but not starting the proxy service
- dynamicProxyRouter.Option.Port = newIncomingPortInt
- }
- sysdb.Write("settings", "inbound", newIncomingPortInt)
- utils.SendOK(w)
- }
- /* Handle Custom Header Rules */
- //List all the custom header defined in this proxy rule
- func HandleCustomHeaderList(w http.ResponseWriter, r *http.Request) {
- epType, err := utils.PostPara(r, "type")
- if err != nil {
- utils.SendErrorResponse(w, "endpoint type not defined")
- return
- }
- domain, err := utils.PostPara(r, "domain")
- if err != nil {
- utils.SendErrorResponse(w, "domain or matching rule not defined")
- return
- }
- var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
- if epType == "root" {
- targetProxyEndpoint = dynamicProxyRouter.Root
- } else {
- ep, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- targetProxyEndpoint = ep
- }
- //List all custom headers
- customHeaderList := targetProxyEndpoint.UserDefinedHeaders
- if customHeaderList == nil {
- customHeaderList = []*dynamicproxy.UserDefinedHeader{}
- }
- js, _ := json.Marshal(customHeaderList)
- utils.SendJSONResponse(w, string(js))
- }
- // Add a new header to the target endpoint
- func HandleCustomHeaderAdd(w http.ResponseWriter, r *http.Request) {
- epType, err := utils.PostPara(r, "type")
- if err != nil {
- utils.SendErrorResponse(w, "endpoint type not defined")
- return
- }
- domain, err := utils.PostPara(r, "domain")
- if err != nil {
- utils.SendErrorResponse(w, "domain or matching rule not defined")
- return
- }
- name, err := utils.PostPara(r, "name")
- if err != nil {
- utils.SendErrorResponse(w, "HTTP header name not set")
- return
- }
- value, err := utils.PostPara(r, "value")
- if err != nil {
- utils.SendErrorResponse(w, "HTTP header value not set")
- return
- }
- var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
- if epType == "root" {
- targetProxyEndpoint = dynamicProxyRouter.Root
- } else {
- ep, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- targetProxyEndpoint = ep
- }
- //Create a new custom header object
- targetProxyEndpoint.AddUserDefinedHeader(name, value)
- //Save it (no need reload as header are not handled by dpcore)
- err = SaveReverseProxyConfig(targetProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, "unable to save update")
- return
- }
- utils.SendOK(w)
- }
- // Remove a header from the target endpoint
- func HandleCustomHeaderRemove(w http.ResponseWriter, r *http.Request) {
- epType, err := utils.PostPara(r, "type")
- if err != nil {
- utils.SendErrorResponse(w, "endpoint type not defined")
- return
- }
- domain, err := utils.PostPara(r, "domain")
- if err != nil {
- utils.SendErrorResponse(w, "domain or matching rule not defined")
- return
- }
- name, err := utils.PostPara(r, "name")
- if err != nil {
- utils.SendErrorResponse(w, "HTTP header name not set")
- return
- }
- var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
- if epType == "root" {
- targetProxyEndpoint = dynamicProxyRouter.Root
- } else {
- ep, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- targetProxyEndpoint = ep
- }
- targetProxyEndpoint.RemoveUserDefinedHeader(name)
- err = SaveReverseProxyConfig(targetProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, "unable to save update")
- return
- }
- utils.SendOK(w)
- }
|