123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653 |
- 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/dynamicproxy/loadbalance"
- "imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
- "imuslab.com/zoraxy/mod/dynamicproxy/rewrite"
- "imuslab.com/zoraxy/mod/netutils"
- "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 := *defaultInboundPort
- autoStartReverseProxy := *defaultEnableInboundTraffic
- if sysdb.KeyExists("settings", "inbound") {
- //Read settings from database
- sysdb.Read("settings", "inbound", &inboundPort)
- if netutils.CheckIfPortOccupied(inboundPort) {
- autoStartReverseProxy = false
- SystemWideLogger.Println("Inbound port ", inboundPort, " is occupied. Change the listening port in the webmin panel and press \"Start Service\" to start reverse proxy service")
- } else {
- SystemWideLogger.Println("Serving inbound port ", inboundPort)
- }
- } else {
- //Default port
- if netutils.CheckIfPortOccupied(inboundPort) {
- autoStartReverseProxy = false
- SystemWideLogger.Println("Port 443 is occupied. Change the listening port in the webmin panel and press \"Start Service\" to start reverse proxy service")
- }
- SystemWideLogger.Println("Inbound port not set. Using default (443)")
- }
- useTls := true
- sysdb.Read("settings", "usetls", &useTls)
- if useTls {
- SystemWideLogger.Println("TLS mode enabled. Serving proxy 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 := true
- if netutils.CheckIfPortOccupied(80) {
- listenOnPort80 = false
- }
- sysdb.Read("settings", "listenP80", &listenOnPort80)
- if listenOnPort80 {
- SystemWideLogger.Println("Port 80 listener enabled")
- } else {
- SystemWideLogger.Println("Port 80 listener disabled")
- }
- forceHttpsRedirect := true
- 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: SYSTEM_VERSION,
- Port: inboundPort,
- UseTls: useTls,
- ForceTLSLatest: forceLatestTLSVersion,
- NoCache: developmentMode,
- ListenOnPort80: listenOnPort80,
- ForceHttpsRedirect: forceHttpsRedirect,
- TlsManager: tlsCertManager,
- RedirectRuleTable: redirectTable,
- GeodbStore: geodbStore,
- StatisticCollector: statisticCollector,
- WebDirectory: *path_webserver,
- AccessController: accessController,
- AutheliaRouter: autheliaRouter,
- LoadBalancer: loadBalancer,
- Logger: SystemWideLogger,
- })
- if err != nil {
- SystemWideLogger.PrintAndLog("proxy-config", "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-config", "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-config", "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
- if autoStartReverseProxy {
- 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
- OnlineStateNotify: loadBalancer.NotifyHostOnlineState, //Notify the load balancer for online state
- Logger: SystemWideLogger, //Logger
- })
- SystemWideLogger.Println("Uptime Monitor background service started")
- }()
- }
- // Toggle the reverse proxy service on and off
- func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) {
- enable, err := utils.PostBool(r, "enable")
- if err != nil {
- utils.SendErrorResponse(w, "enable not defined")
- return
- }
- if enable {
- 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?
- skipTlsValidation, _ := utils.PostBool(r, "tlsval")
- //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?
- requireBasicAuth, _ := utils.PostBool(r, "bauth")
- //Use sticky session?
- useStickySession, _ := utils.PostBool(r, "stickysess")
- // Require Rate Limiting?
- requireRateLimit := false
- proxyRateLimit := 1000
- requireRateLimit, err = utils.PostBool(r, "rate")
- if err != nil {
- requireRateLimit = false
- }
- if requireRateLimit {
- proxyRateLimit, err = utils.PostInt(r, "ratenum")
- if err != nil {
- proxyRateLimit = 0
- }
- if err != nil {
- utils.SendErrorResponse(w, "invalid rate limit number")
- return
- }
- if proxyRateLimit <= 0 {
- utils.SendErrorResponse(w, "rate limit number must be greater than 0")
- return
- }
- }
- // 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),
- })
- }
- }
- tagStr, _ := utils.PostPara(r, "tags")
- tags := []string{}
- if tagStr != "" {
- tags = strings.Split(tagStr, ",")
- for i := range tags {
- tags[i] = strings.TrimSpace(tags[i])
- }
- }
- // Remove empty tags
- filteredTags := []string{}
- for _, tag := range tags {
- if tag != "" {
- filteredTags = append(filteredTags, tag)
- }
- }
- tags = filteredTags
- 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 default authenticaion provider
- authMethod := dynamicproxy.AuthMethodNone
- if requireBasicAuth {
- authMethod = dynamicproxy.AuthMethodBasic
- }
- thisAuthenticationProvider := dynamicproxy.AuthenticationProvider{
- AuthMethod: authMethod,
- BasicAuthCredentials: basicAuthCredentials,
- BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
- }
- //Generate a proxy endpoint object
- thisProxyEndpoint := dynamicproxy.ProxyEndpoint{
- //I/O
- ProxyType: dynamicproxy.ProxyTypeHost,
- RootOrMatchingDomain: rootOrMatchingDomain,
- MatchingDomainAlias: aliasHostnames,
- ActiveOrigins: []*loadbalance.Upstream{
- {
- OriginIpOrDomain: endpoint,
- RequireTLS: useTLS,
- SkipCertValidations: skipTlsValidation,
- SkipWebSocketOriginCheck: bypassWebsocketOriginCheck,
- Weight: 1,
- },
- },
- InactiveOrigins: []*loadbalance.Upstream{},
- UseStickySession: useStickySession,
- //TLS
- BypassGlobalTLS: useBypassGlobalTLS,
- AccessFilterUUID: accessRuleID,
- //VDir
- VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
- //Custom headers
- //Auth
- AuthenticationProvider: &thisAuthenticationProvider,
- //Header Rewrite
- HeaderRewriteRules: dynamicproxy.GetDefaultHeaderRewriteRules(),
- //Default Site
- DefaultSiteOption: 0,
- DefaultSiteValue: "",
- // Rate Limit
- RequireRateLimit: requireRateLimit,
- RateLimit: int64(proxyRateLimit),
- Tags: tags,
- }
- 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.ProxyTypeRoot,
- RootOrMatchingDomain: "/",
- ActiveOrigins: []*loadbalance.Upstream{
- {
- OriginIpOrDomain: endpoint,
- RequireTLS: useTLS,
- SkipCertValidations: true,
- SkipWebSocketOriginCheck: true,
- Weight: 1,
- },
- },
- InactiveOrigins: []*loadbalance.Upstream{},
- BypassGlobalTLS: false,
- DefaultSiteOption: defaultSiteOption,
- DefaultSiteValue: dsVal,
- }
- preparedRootProxyRoute, err := dynamicProxyRouter.PrepareProxyRoute(&rootRoutingEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, "unable to prepare root routing: "+err.Error())
- return
- }
- err = dynamicProxyRouter.SetProxyRouteAsRoot(preparedRootProxyRoute)
- if err != nil {
- utils.SendErrorResponse(w, "unable to update default site: "+err.Error())
- return
- }
- 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-config", "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
- }
- tls, _ := utils.PostPara(r, "tls")
- if tls == "" {
- tls = "false"
- }
- useStickySession, _ := utils.PostBool(r, "ss")
- //Load bypass TLS option
- bpgtls, _ := utils.PostPara(r, "bpgtls")
- if bpgtls == "" {
- bpgtls = "false"
- }
- bypassGlobalTLS := (bpgtls == "true")
- //Disable uptime monitor
- disbleUtm, err := utils.PostBool(r, "dutm")
- if err != nil {
- disbleUtm = false
- }
- // Auth Provider
- authProviderTypeStr, _ := utils.PostPara(r, "authprovider")
- if authProviderTypeStr == "" {
- authProviderTypeStr = "0"
- }
- authProviderType, err := strconv.Atoi(authProviderTypeStr)
- if err != nil {
- utils.SendErrorResponse(w, "Invalid auth provider type")
- return
- }
- // Rate Limiting?
- rl, _ := utils.PostPara(r, "rate")
- if rl == "" {
- rl = "false"
- }
- requireRateLimit := (rl == "true")
- rlnum, _ := utils.PostPara(r, "ratenum")
- if rlnum == "" {
- rlnum = "0"
- }
- proxyRateLimit, err := strconv.ParseInt(rlnum, 10, 64)
- if err != nil {
- utils.SendErrorResponse(w, "invalid rate limit number")
- return
- }
- if requireRateLimit && proxyRateLimit <= 0 {
- utils.SendErrorResponse(w, "rate limit number must be greater than 0")
- return
- } else if proxyRateLimit < 0 {
- proxyRateLimit = 1000
- }
- //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
- }
- tagStr, _ := utils.PostPara(r, "tags")
- tags := []string{}
- if tagStr != "" {
- tags = strings.Split(tagStr, ",")
- for i := range tags {
- tags[i] = strings.TrimSpace(tags[i])
- }
- }
- //Generate a new proxyEndpoint from the new config
- newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
- newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS
- if newProxyEndpoint.AuthenticationProvider == nil {
- newProxyEndpoint.AuthenticationProvider = &dynamicproxy.AuthenticationProvider{
- AuthMethod: dynamicproxy.AuthMethodNone,
- BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{},
- BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
- }
- }
- if authProviderType == 1 {
- newProxyEndpoint.AuthenticationProvider.AuthMethod = dynamicproxy.AuthMethodBasic
- } else if authProviderType == 2 {
- newProxyEndpoint.AuthenticationProvider.AuthMethod = dynamicproxy.AuthMethodAuthelia
- } else if authProviderType == 3 {
- newProxyEndpoint.AuthenticationProvider.AuthMethod = dynamicproxy.AuthMethodOauth2
- } else {
- newProxyEndpoint.AuthenticationProvider.AuthMethod = dynamicproxy.AuthMethodNone
- }
- newProxyEndpoint.RequireRateLimit = requireRateLimit
- newProxyEndpoint.RateLimit = proxyRateLimit
- newProxyEndpoint.UseStickySession = useStickySession
- newProxyEndpoint.DisableUptimeMonitor = disbleUtm
- newProxyEndpoint.Tags = tags
- //Prepare to replace the current routing rule
- readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- targetProxyEntry.Remove()
- loadBalancer.ResetSessions()
- dynamicProxyRouter.AddProxyRouteToRuntime(readyRoutingRule)
- //Save it to file
- SaveReverseProxyConfig(newProxyEndpoint)
- //Update uptime monitor targets
- 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-config", "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-config", "Unable to save alias update", err)
- }
- utils.SendOK(w)
- }
- func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
- ep, err := utils.PostPara(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 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.AuthenticationProvider.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.AuthenticationProvider.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.AuthenticationProvider.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.AuthenticationProvider.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.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{
- 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.AuthenticationProvider.BasicAuthExceptionRules {
- if thisExceptionalRule.PathPrefix != matchingPrefix {
- newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule)
- } else {
- matchingExists = true
- }
- }
- if !matchingExists {
- utils.SendErrorResponse(w, "target matching rule not exists")
- return
- }
- targetProxy.AuthenticationProvider.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
- }
- //Update uptime monitor
- UpdateUptimeMonitorTargets()
- 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
- }
- epname = strings.ToLower(strings.TrimSpace(epname))
- 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.AuthenticationProvider.BasicAuthCredentials {
- cleanedCredentials = append(cleanedCredentials, &dynamicproxy.BasicAuthCredentials{
- Username: user.Username,
- PasswordHash: "",
- })
- }
- thisEndpoint.AuthenticationProvider.BasicAuthCredentials = cleanedCredentials
- results = append(results, thisEndpoint)
- 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" {
- 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) {
- if r.Method == http.MethodGet {
- //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 r.Method == http.MethodPost {
- enabled, err := utils.PostPara(r, "enable")
- if err != nil {
- utils.SendErrorResponse(w, "enable state not set")
- return
- }
- 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)
- } else {
- http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
- }
- }
- // Handle https redirect
- func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
- if r.Method == http.MethodGet {
- 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 r.Method == http.MethodPost {
- useRedirect, err := utils.PostBool(r, "set")
- if err != nil {
- utils.SendErrorResponse(w, "status not set")
- return
- }
- if dynamicProxyRouter.Option.Port == 80 {
- utils.SendErrorResponse(w, "This option is not available when listening on port 80")
- return
- }
- if useRedirect {
- sysdb.Write("settings", "redirect", true)
- SystemWideLogger.Println("Updating force HTTPS redirection to true")
- dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
- } else {
- sysdb.Write("settings", "redirect", false)
- SystemWideLogger.Println("Updating force HTTPS redirection to false")
- dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
- }
- utils.SendOK(w)
- } else {
- http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
- }
- }
- // 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
- }
- rootProxyTargetOrigin := ""
- if len(dynamicProxyRouter.Root.ActiveOrigins) > 0 {
- rootProxyTargetOrigin = dynamicProxyRouter.Root.ActiveOrigins[0].OriginIpOrDomain
- }
- //Check if it is identical as proxy root (recursion!)
- if dynamicProxyRouter.Root == nil || rootProxyTargetOrigin == "" {
- //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(rootProxyTargetOrigin, "/")
- 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
- time.Sleep(1 * time.Second) //Fixed start fail issue
- 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.GetPara(r, "type")
- if err != nil {
- utils.SendErrorResponse(w, "endpoint type not defined")
- return
- }
- domain, err := utils.GetPara(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.HeaderRewriteRules.UserDefinedHeaders
- if customHeaderList == nil {
- customHeaderList = []*rewrite.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) {
- rewriteType, err := utils.PostPara(r, "type")
- if err != nil {
- utils.SendErrorResponse(w, "rewriteType not defined")
- return
- }
- domain, err := utils.PostPara(r, "domain")
- if err != nil {
- utils.SendErrorResponse(w, "domain or matching rule not defined")
- return
- }
- direction, err := utils.PostPara(r, "direction")
- if err != nil {
- utils.SendErrorResponse(w, "HTTP modifiy direction not set")
- 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 && rewriteType == "add" {
- utils.SendErrorResponse(w, "HTTP header value not set")
- return
- }
- targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- //Create a Custom Header Definition type
- var rewriteDirection rewrite.HeaderDirection
- if direction == "toOrigin" {
- rewriteDirection = rewrite.HeaderDirection_ZoraxyToUpstream
- } else if direction == "toClient" {
- rewriteDirection = rewrite.HeaderDirection_ZoraxyToDownstream
- } else {
- //Unknown direction
- utils.SendErrorResponse(w, "header rewrite direction not supported")
- return
- }
- isRemove := false
- if rewriteType == "remove" {
- isRemove = true
- }
- headerRewriteDefinition := rewrite.UserDefinedHeader{
- Key: name,
- Value: value,
- Direction: rewriteDirection,
- IsRemove: isRemove,
- }
- //Create a new custom header object
- err = targetProxyEndpoint.AddUserDefinedHeader(&headerRewriteDefinition)
- if err != nil {
- utils.SendErrorResponse(w, "unable to add header rewrite rule: "+err.Error())
- return
- }
- //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) {
- 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
- }
- targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- err = targetProxyEndpoint.RemoveUserDefinedHeader(name)
- if err != nil {
- utils.SendErrorResponse(w, "unable to remove header rewrite rule: "+err.Error())
- return
- }
- err = SaveReverseProxyConfig(targetProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, "unable to save update")
- return
- }
- utils.SendOK(w)
- }
- func HandleHostOverwrite(w http.ResponseWriter, r *http.Request) {
- domain, err := utils.PostPara(r, "domain")
- if err != nil {
- domain, err = utils.GetPara(r, "domain")
- if err != nil {
- utils.SendErrorResponse(w, "domain or matching rule not defined")
- return
- }
- }
- //Get the proxy endpoint object dedicated to this domain
- targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- if r.Method == http.MethodGet {
- //Get the current host header
- js, _ := json.Marshal(targetProxyEndpoint.HeaderRewriteRules.RequestHostOverwrite)
- utils.SendJSONResponse(w, string(js))
- } else if r.Method == http.MethodPost {
- //Set the new host header
- newHostname, _ := utils.PostPara(r, "hostname")
- //As this will require change in the proxy instance we are running
- //we need to clone and respawn this proxy endpoint
- newProxyEndpoint := targetProxyEndpoint.Clone()
- newProxyEndpoint.HeaderRewriteRules.RequestHostOverwrite = newHostname
- //Save proxy endpoint
- err = SaveReverseProxyConfig(newProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Spawn a new endpoint with updated dpcore
- preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Remove the old endpoint
- err = targetProxyEndpoint.Remove()
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Add the newly prepared endpoint to runtime
- err = dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Print log message
- if newHostname != "" {
- SystemWideLogger.Println("Updated " + domain + " hostname overwrite to: " + newHostname)
- } else {
- SystemWideLogger.Println("Removed " + domain + " hostname overwrite")
- }
- utils.SendOK(w)
- } else {
- //Invalid method
- http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
- }
- }
- // HandleHopByHop get and set the hop by hop remover state
- // note that it shows the DISABLE STATE of hop-by-hop remover, not the enable state
- func HandleHopByHop(w http.ResponseWriter, r *http.Request) {
- domain, err := utils.PostPara(r, "domain")
- if err != nil {
- domain, err = utils.GetPara(r, "domain")
- if err != nil {
- utils.SendErrorResponse(w, "domain or matching rule not defined")
- return
- }
- }
- targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- if r.Method == http.MethodGet {
- //Get the current hop by hop header state
- js, _ := json.Marshal(!targetProxyEndpoint.HeaderRewriteRules.DisableHopByHopHeaderRemoval)
- utils.SendJSONResponse(w, string(js))
- } else if r.Method == http.MethodPost {
- //Set the hop by hop header state
- enableHopByHopRemover, _ := utils.PostBool(r, "removeHopByHop")
- //As this will require change in the proxy instance we are running
- //we need to clone and respawn this proxy endpoint
- newProxyEndpoint := targetProxyEndpoint.Clone()
- //Storage file use false as default, so disable removal = not enable remover
- newProxyEndpoint.HeaderRewriteRules.DisableHopByHopHeaderRemoval = !enableHopByHopRemover
- //Save proxy endpoint
- err = SaveReverseProxyConfig(newProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Spawn a new endpoint with updated dpcore
- preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Remove the old endpoint
- err = targetProxyEndpoint.Remove()
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Add the newly prepared endpoint to runtime
- err = dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, err.Error())
- return
- }
- //Print log message
- if enableHopByHopRemover {
- SystemWideLogger.Println("Enabled hop-by-hop headers removal on " + domain)
- } else {
- SystemWideLogger.Println("Disabled hop-by-hop headers removal on " + domain)
- }
- utils.SendOK(w)
- } else {
- http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
- }
- }
- // Handle view or edit HSTS states
- func HandleHSTSState(w http.ResponseWriter, r *http.Request) {
- domain, err := utils.PostPara(r, "domain")
- if err != nil {
- domain, err = utils.GetPara(r, "domain")
- if err != nil {
- utils.SendErrorResponse(w, "domain or matching rule not defined")
- return
- }
- }
- targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- if r.Method == http.MethodGet {
- //Return current HSTS enable state
- hstsAge := targetProxyEndpoint.HeaderRewriteRules.HSTSMaxAge
- js, _ := json.Marshal(hstsAge)
- utils.SendJSONResponse(w, string(js))
- return
- } else if r.Method == http.MethodPost {
- newMaxAge, err := utils.PostInt(r, "maxage")
- if err != nil {
- utils.SendErrorResponse(w, "maxage not defeined")
- return
- }
- if newMaxAge == 0 || newMaxAge >= 31536000 {
- targetProxyEndpoint.HeaderRewriteRules.HSTSMaxAge = int64(newMaxAge)
- err = SaveReverseProxyConfig(targetProxyEndpoint)
- if err != nil {
- utils.SendErrorResponse(w, "save HSTS state failed: "+err.Error())
- return
- }
- targetProxyEndpoint.UpdateToRuntime()
- } else {
- utils.SendErrorResponse(w, "invalid max age given")
- return
- }
- utils.SendOK(w)
- return
- }
- http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
- }
- // HandlePermissionPolicy handle read or write to permission policy
- func HandlePermissionPolicy(w http.ResponseWriter, r *http.Request) {
- domain, err := utils.PostPara(r, "domain")
- if err != nil {
- domain, err = utils.GetPara(r, "domain")
- if err != nil {
- utils.SendErrorResponse(w, "domain or matching rule not defined")
- return
- }
- }
- targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- if r.Method == http.MethodGet {
- type CurrentPolicyState struct {
- PPEnabled bool
- CurrentPolicy *permissionpolicy.PermissionsPolicy
- }
- currentPolicy := permissionpolicy.GetDefaultPermissionPolicy()
- if targetProxyEndpoint.HeaderRewriteRules.PermissionPolicy != nil {
- currentPolicy = targetProxyEndpoint.HeaderRewriteRules.PermissionPolicy
- }
- result := CurrentPolicyState{
- PPEnabled: targetProxyEndpoint.HeaderRewriteRules.EnablePermissionPolicyHeader,
- CurrentPolicy: currentPolicy,
- }
- js, _ := json.Marshal(result)
- utils.SendJSONResponse(w, string(js))
- return
- } else if r.Method == http.MethodPost {
- //Update the enable state of permission policy
- enableState, err := utils.PostBool(r, "enable")
- if err != nil {
- utils.SendErrorResponse(w, "invalid enable state given")
- return
- }
- targetProxyEndpoint.HeaderRewriteRules.EnablePermissionPolicyHeader = enableState
- SaveReverseProxyConfig(targetProxyEndpoint)
- targetProxyEndpoint.UpdateToRuntime()
- utils.SendOK(w)
- return
- } else if r.Method == http.MethodPut {
- //Store the new permission policy
- newPermissionPolicyJSONString, err := utils.PostPara(r, "pp")
- if err != nil {
- utils.SendErrorResponse(w, "missing pp (permission policy) paramter")
- return
- }
- //Parse the permission policy from JSON string
- newPermissionPolicy := permissionpolicy.GetDefaultPermissionPolicy()
- err = json.Unmarshal([]byte(newPermissionPolicyJSONString), &newPermissionPolicy)
- if err != nil {
- utils.SendErrorResponse(w, "permission policy parse error: "+err.Error())
- return
- }
- //Save it to file
- targetProxyEndpoint.HeaderRewriteRules.PermissionPolicy = newPermissionPolicy
- SaveReverseProxyConfig(targetProxyEndpoint)
- targetProxyEndpoint.UpdateToRuntime()
- utils.SendOK(w)
- return
- }
- http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
- }
- func HandleWsHeaderBehavior(w http.ResponseWriter, r *http.Request) {
- domain, err := utils.PostPara(r, "domain")
- if err != nil {
- domain, err = utils.GetPara(r, "domain")
- if err != nil {
- utils.SendErrorResponse(w, "domain or matching rule not defined")
- return
- }
- }
- targetProxyEndpoint, err := dynamicProxyRouter.LoadProxy(domain)
- if err != nil {
- utils.SendErrorResponse(w, "target endpoint not exists")
- return
- }
- if r.Method == http.MethodGet {
- js, _ := json.Marshal(targetProxyEndpoint.EnableWebsocketCustomHeaders)
- utils.SendJSONResponse(w, string(js))
- } else if r.Method == http.MethodPost {
- enableWsHeader, err := utils.PostBool(r, "enable")
- if err != nil {
- utils.SendErrorResponse(w, "invalid enable state given")
- return
- }
- targetProxyEndpoint.EnableWebsocketCustomHeaders = enableWsHeader
- SaveReverseProxyConfig(targetProxyEndpoint)
- targetProxyEndpoint.UpdateToRuntime()
- utils.SendOK(w)
- } else {
- http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
- }
- }
|