reverseproxy.go 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  1. package main
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "path/filepath"
  6. "sort"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "imuslab.com/zoraxy/mod/auth"
  11. "imuslab.com/zoraxy/mod/dynamicproxy"
  12. "imuslab.com/zoraxy/mod/uptime"
  13. "imuslab.com/zoraxy/mod/utils"
  14. )
  15. var (
  16. dynamicProxyRouter *dynamicproxy.Router
  17. )
  18. // Add user customizable reverse proxy
  19. func ReverseProxtInit() {
  20. /*
  21. Load Reverse Proxy Global Settings
  22. */
  23. inboundPort := 80
  24. if sysdb.KeyExists("settings", "inbound") {
  25. sysdb.Read("settings", "inbound", &inboundPort)
  26. SystemWideLogger.Println("Serving inbound port ", inboundPort)
  27. } else {
  28. SystemWideLogger.Println("Inbound port not set. Using default (80)")
  29. }
  30. useTls := false
  31. sysdb.Read("settings", "usetls", &useTls)
  32. if useTls {
  33. SystemWideLogger.Println("TLS mode enabled. Serving proxxy request with TLS")
  34. } else {
  35. SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http")
  36. }
  37. forceLatestTLSVersion := false
  38. sysdb.Read("settings", "forceLatestTLS", &forceLatestTLSVersion)
  39. if forceLatestTLSVersion {
  40. SystemWideLogger.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2")
  41. } else {
  42. SystemWideLogger.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0")
  43. }
  44. developmentMode := false
  45. sysdb.Read("settings", "devMode", &developmentMode)
  46. if useTls {
  47. SystemWideLogger.Println("Development mode enabled. Using no-store Cache Control policy")
  48. } else {
  49. SystemWideLogger.Println("Development mode disabled. Proxying with default Cache Control policy")
  50. }
  51. listenOnPort80 := false
  52. sysdb.Read("settings", "listenP80", &listenOnPort80)
  53. if listenOnPort80 {
  54. SystemWideLogger.Println("Port 80 listener enabled")
  55. } else {
  56. SystemWideLogger.Println("Port 80 listener disabled")
  57. }
  58. forceHttpsRedirect := false
  59. sysdb.Read("settings", "redirect", &forceHttpsRedirect)
  60. if forceHttpsRedirect {
  61. SystemWideLogger.Println("Force HTTPS mode enabled")
  62. //Port 80 listener must be enabled to perform http -> https redirect
  63. listenOnPort80 = true
  64. } else {
  65. SystemWideLogger.Println("Force HTTPS mode disabled")
  66. }
  67. /*
  68. Create a new proxy object
  69. The DynamicProxy is the parent of all reverse proxy handlers,
  70. use for managemening and provide functions to access proxy handlers
  71. */
  72. dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
  73. HostUUID: nodeUUID,
  74. HostVersion: version,
  75. Port: inboundPort,
  76. UseTls: useTls,
  77. ForceTLSLatest: forceLatestTLSVersion,
  78. NoCache: developmentMode,
  79. ListenOnPort80: listenOnPort80,
  80. ForceHttpsRedirect: forceHttpsRedirect,
  81. TlsManager: tlsCertManager,
  82. RedirectRuleTable: redirectTable,
  83. GeodbStore: geodbStore,
  84. StatisticCollector: statisticCollector,
  85. WebDirectory: *staticWebServerRoot,
  86. AccessController: accessController,
  87. })
  88. if err != nil {
  89. SystemWideLogger.PrintAndLog("Proxy", "Unable to create dynamic proxy router", err)
  90. return
  91. }
  92. dynamicProxyRouter = dprouter
  93. /*
  94. Load all conf from files
  95. */
  96. confs, _ := filepath.Glob("./conf/proxy/*.config")
  97. for _, conf := range confs {
  98. err := LoadReverseProxyConfig(conf)
  99. if err != nil {
  100. SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err)
  101. return
  102. }
  103. }
  104. if dynamicProxyRouter.Root == nil {
  105. //Root config not set (new deployment?), use internal static web server as root
  106. defaultRootRouter, err := GetDefaultRootConfig()
  107. if err != nil {
  108. SystemWideLogger.PrintAndLog("Proxy", "Failed to generate default root routing", err)
  109. return
  110. }
  111. dynamicProxyRouter.SetProxyRouteAsRoot(defaultRootRouter)
  112. }
  113. //Start Service
  114. //Not sure why but delay must be added if you have another
  115. //reverse proxy server in front of this service
  116. time.Sleep(300 * time.Millisecond)
  117. dynamicProxyRouter.StartProxyService()
  118. SystemWideLogger.Println("Dynamic Reverse Proxy service started")
  119. //Add all proxy services to uptime monitor
  120. //Create a uptime monitor service
  121. go func() {
  122. //This must be done in go routine to prevent blocking on system startup
  123. uptimeMonitor, _ = uptime.NewUptimeMonitor(&uptime.Config{
  124. Targets: GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter),
  125. Interval: 300, //5 minutes
  126. MaxRecordsStore: 288, //1 day
  127. })
  128. SystemWideLogger.Println("Uptime Monitor background service started")
  129. }()
  130. }
  131. func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) {
  132. enable, _ := utils.PostPara(r, "enable") //Support root, vdir and subd
  133. if enable == "true" {
  134. err := dynamicProxyRouter.StartProxyService()
  135. if err != nil {
  136. utils.SendErrorResponse(w, err.Error())
  137. return
  138. }
  139. } else {
  140. //Check if it is loopback
  141. if dynamicProxyRouter.IsProxiedSubdomain(r) {
  142. //Loopback routing. Turning it off will make the user lost control
  143. //of the whole system. Do not allow shutdown
  144. utils.SendErrorResponse(w, "Unable to shutdown in loopback rp mode. Remove proxy rules for management interface and retry.")
  145. return
  146. }
  147. err := dynamicProxyRouter.StopProxyService()
  148. if err != nil {
  149. utils.SendErrorResponse(w, err.Error())
  150. return
  151. }
  152. }
  153. utils.SendOK(w)
  154. }
  155. func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
  156. eptype, err := utils.PostPara(r, "type") //Support root and host
  157. if err != nil {
  158. utils.SendErrorResponse(w, "type not defined")
  159. return
  160. }
  161. endpoint, err := utils.PostPara(r, "ep")
  162. if err != nil {
  163. utils.SendErrorResponse(w, "endpoint not defined")
  164. return
  165. }
  166. tls, _ := utils.PostPara(r, "tls")
  167. if tls == "" {
  168. tls = "false"
  169. }
  170. useTLS := (tls == "true")
  171. //Bypass global TLS value / allow direct access from port 80?
  172. bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS")
  173. if bypassGlobalTLS == "" {
  174. bypassGlobalTLS = "false"
  175. }
  176. useBypassGlobalTLS := bypassGlobalTLS == "true"
  177. //Enable TLS validation?
  178. stv, _ := utils.PostPara(r, "tlsval")
  179. if stv == "" {
  180. stv = "false"
  181. }
  182. skipTlsValidation := (stv == "true")
  183. //Get access rule ID
  184. accessRuleID, _ := utils.PostPara(r, "access")
  185. if accessRuleID == "" {
  186. accessRuleID = "default"
  187. }
  188. if !accessController.AccessRuleExists(accessRuleID) {
  189. utils.SendErrorResponse(w, "invalid access rule ID selected")
  190. return
  191. }
  192. //Require basic auth?
  193. rba, _ := utils.PostPara(r, "bauth")
  194. if rba == "" {
  195. rba = "false"
  196. }
  197. requireBasicAuth := (rba == "true")
  198. // Bypass WebSocket Origin Check
  199. strbpwsorg, _ := utils.PostPara(r, "bpwsorg")
  200. if strbpwsorg == "" {
  201. strbpwsorg = "false"
  202. }
  203. bypassWebsocketOriginCheck := (strbpwsorg == "true")
  204. //Prase the basic auth to correct structure
  205. cred, _ := utils.PostPara(r, "cred")
  206. basicAuthCredentials := []*dynamicproxy.BasicAuthCredentials{}
  207. if requireBasicAuth {
  208. preProcessCredentials := []*dynamicproxy.BasicAuthUnhashedCredentials{}
  209. err = json.Unmarshal([]byte(cred), &preProcessCredentials)
  210. if err != nil {
  211. utils.SendErrorResponse(w, "invalid user credentials")
  212. return
  213. }
  214. //Check if there are empty password credentials
  215. for _, credObj := range preProcessCredentials {
  216. if strings.TrimSpace(credObj.Password) == "" {
  217. utils.SendErrorResponse(w, credObj.Username+" has empty password")
  218. return
  219. }
  220. }
  221. //Convert and hash the passwords
  222. for _, credObj := range preProcessCredentials {
  223. basicAuthCredentials = append(basicAuthCredentials, &dynamicproxy.BasicAuthCredentials{
  224. Username: credObj.Username,
  225. PasswordHash: auth.Hash(credObj.Password),
  226. })
  227. }
  228. }
  229. var proxyEndpointCreated *dynamicproxy.ProxyEndpoint
  230. if eptype == "host" {
  231. rootOrMatchingDomain, err := utils.PostPara(r, "rootname")
  232. if err != nil {
  233. utils.SendErrorResponse(w, "subdomain not defined")
  234. return
  235. }
  236. thisProxyEndpoint := dynamicproxy.ProxyEndpoint{
  237. //I/O
  238. ProxyType: dynamicproxy.ProxyType_Host,
  239. RootOrMatchingDomain: rootOrMatchingDomain,
  240. Domain: endpoint,
  241. //TLS
  242. RequireTLS: useTLS,
  243. BypassGlobalTLS: useBypassGlobalTLS,
  244. SkipCertValidations: skipTlsValidation,
  245. SkipWebSocketOriginCheck: bypassWebsocketOriginCheck,
  246. AccessFilterUUID: accessRuleID,
  247. //VDir
  248. VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
  249. //Custom headers
  250. UserDefinedHeaders: []*dynamicproxy.UserDefinedHeader{},
  251. //Auth
  252. RequireBasicAuth: requireBasicAuth,
  253. BasicAuthCredentials: basicAuthCredentials,
  254. BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
  255. DefaultSiteOption: 0,
  256. DefaultSiteValue: "",
  257. }
  258. preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint)
  259. if err != nil {
  260. utils.SendErrorResponse(w, "unable to prepare proxy route to target endpoint: "+err.Error())
  261. return
  262. }
  263. dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint)
  264. proxyEndpointCreated = &thisProxyEndpoint
  265. } else if eptype == "root" {
  266. //Get the default site options and target
  267. dsOptString, err := utils.PostPara(r, "defaultSiteOpt")
  268. if err != nil {
  269. utils.SendErrorResponse(w, "default site action not defined")
  270. return
  271. }
  272. var defaultSiteOption int = 1
  273. opt, err := strconv.Atoi(dsOptString)
  274. if err != nil {
  275. utils.SendErrorResponse(w, "invalid default site option")
  276. return
  277. }
  278. defaultSiteOption = opt
  279. dsVal, err := utils.PostPara(r, "defaultSiteVal")
  280. if err != nil && (defaultSiteOption == 1 || defaultSiteOption == 2) {
  281. //Reverse proxy or redirect, must require value to be set
  282. utils.SendErrorResponse(w, "target not defined")
  283. return
  284. }
  285. //Write the root options to file
  286. rootRoutingEndpoint := dynamicproxy.ProxyEndpoint{
  287. ProxyType: dynamicproxy.ProxyType_Root,
  288. RootOrMatchingDomain: "/",
  289. Domain: endpoint,
  290. RequireTLS: useTLS,
  291. BypassGlobalTLS: false,
  292. SkipCertValidations: false,
  293. SkipWebSocketOriginCheck: true,
  294. DefaultSiteOption: defaultSiteOption,
  295. DefaultSiteValue: dsVal,
  296. }
  297. preparedRootProxyRoute, err := dynamicProxyRouter.PrepareProxyRoute(&rootRoutingEndpoint)
  298. if err != nil {
  299. utils.SendErrorResponse(w, "unable to prepare root routing: "+err.Error())
  300. return
  301. }
  302. dynamicProxyRouter.SetProxyRouteAsRoot(preparedRootProxyRoute)
  303. proxyEndpointCreated = &rootRoutingEndpoint
  304. } else {
  305. //Invalid eptype
  306. utils.SendErrorResponse(w, "invalid endpoint type")
  307. return
  308. }
  309. //Save the config to file
  310. err = SaveReverseProxyConfig(proxyEndpointCreated)
  311. if err != nil {
  312. SystemWideLogger.PrintAndLog("Proxy", "Unable to save new proxy rule to file", err)
  313. return
  314. }
  315. //Update utm if exists
  316. UpdateUptimeMonitorTargets()
  317. utils.SendOK(w)
  318. }
  319. /*
  320. ReverseProxyHandleEditEndpoint handles proxy endpoint edit
  321. (host only, for root use Default Site page to edit)
  322. This endpoint do not handle basic auth credential update.
  323. The credential will be loaded from old config and reused
  324. */
  325. func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
  326. rootNameOrMatchingDomain, err := utils.PostPara(r, "rootname")
  327. if err != nil {
  328. utils.SendErrorResponse(w, "Target proxy rule not defined")
  329. return
  330. }
  331. endpoint, err := utils.PostPara(r, "ep")
  332. if err != nil {
  333. utils.SendErrorResponse(w, "endpoint not defined")
  334. return
  335. }
  336. tls, _ := utils.PostPara(r, "tls")
  337. if tls == "" {
  338. tls = "false"
  339. }
  340. useTLS := (tls == "true")
  341. stv, _ := utils.PostPara(r, "tlsval")
  342. if stv == "" {
  343. stv = "false"
  344. }
  345. skipTlsValidation := (stv == "true")
  346. //Load bypass TLS option
  347. bpgtls, _ := utils.PostPara(r, "bpgtls")
  348. if bpgtls == "" {
  349. bpgtls = "false"
  350. }
  351. bypassGlobalTLS := (bpgtls == "true")
  352. // Basic Auth
  353. rba, _ := utils.PostPara(r, "bauth")
  354. if rba == "" {
  355. rba = "false"
  356. }
  357. requireBasicAuth := (rba == "true")
  358. // Bypass WebSocket Origin Check
  359. strbpwsorg, _ := utils.PostPara(r, "bpwsorg")
  360. if strbpwsorg == "" {
  361. strbpwsorg = "false"
  362. }
  363. bypassWebsocketOriginCheck := (strbpwsorg == "true")
  364. //Load the previous basic auth credentials from current proxy rules
  365. targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
  366. if err != nil {
  367. utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
  368. return
  369. }
  370. //Generate a new proxyEndpoint from the new config
  371. newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
  372. newProxyEndpoint.Domain = endpoint
  373. newProxyEndpoint.RequireTLS = useTLS
  374. newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS
  375. newProxyEndpoint.SkipCertValidations = skipTlsValidation
  376. newProxyEndpoint.RequireBasicAuth = requireBasicAuth
  377. newProxyEndpoint.SkipWebSocketOriginCheck = bypassWebsocketOriginCheck
  378. //Prepare to replace the current routing rule
  379. readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
  380. if err != nil {
  381. utils.SendErrorResponse(w, err.Error())
  382. return
  383. }
  384. targetProxyEntry.Remove()
  385. dynamicProxyRouter.AddProxyRouteToRuntime(readyRoutingRule)
  386. //Save it to file
  387. SaveReverseProxyConfig(newProxyEndpoint)
  388. //Update uptime monitor
  389. UpdateUptimeMonitorTargets()
  390. utils.SendOK(w)
  391. }
  392. func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
  393. ep, err := utils.GetPara(r, "ep")
  394. if err != nil {
  395. utils.SendErrorResponse(w, "Invalid ep given")
  396. return
  397. }
  398. //Remove the config from runtime
  399. err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ep)
  400. if err != nil {
  401. utils.SendErrorResponse(w, err.Error())
  402. return
  403. }
  404. //Remove the config from file
  405. err = RemoveReverseProxyConfig(ep)
  406. if err != nil {
  407. utils.SendErrorResponse(w, err.Error())
  408. return
  409. }
  410. //Update utm if exists
  411. if uptimeMonitor != nil {
  412. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  413. uptimeMonitor.CleanRecords()
  414. }
  415. //Update uptime monitor
  416. UpdateUptimeMonitorTargets()
  417. utils.SendOK(w)
  418. }
  419. /*
  420. Handle update request for basic auth credential
  421. Require paramter: ep (Endpoint) and pytype (proxy Type)
  422. if request with GET, the handler will return current credentials
  423. on this endpoint by its username
  424. if request is POST, the handler will write the results to proxy config
  425. */
  426. func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
  427. if r.Method == http.MethodGet {
  428. ep, err := utils.GetPara(r, "ep")
  429. if err != nil {
  430. utils.SendErrorResponse(w, "Invalid ep given")
  431. return
  432. }
  433. //Load the target proxy object from router
  434. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  435. if err != nil {
  436. utils.SendErrorResponse(w, err.Error())
  437. return
  438. }
  439. usernames := []string{}
  440. for _, cred := range targetProxy.BasicAuthCredentials {
  441. usernames = append(usernames, cred.Username)
  442. }
  443. js, _ := json.Marshal(usernames)
  444. utils.SendJSONResponse(w, string(js))
  445. } else if r.Method == http.MethodPost {
  446. //Write to target
  447. ep, err := utils.PostPara(r, "ep")
  448. if err != nil {
  449. utils.SendErrorResponse(w, "Invalid ep given")
  450. return
  451. }
  452. creds, err := utils.PostPara(r, "creds")
  453. if err != nil {
  454. utils.SendErrorResponse(w, "Invalid ptype given")
  455. return
  456. }
  457. //Load the target proxy object from router
  458. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  459. if err != nil {
  460. utils.SendErrorResponse(w, err.Error())
  461. return
  462. }
  463. //Try to marshal the content of creds into the suitable structure
  464. newCredentials := []*dynamicproxy.BasicAuthUnhashedCredentials{}
  465. err = json.Unmarshal([]byte(creds), &newCredentials)
  466. if err != nil {
  467. utils.SendErrorResponse(w, "Malformed credential data")
  468. return
  469. }
  470. //Merge the credentials into the original config
  471. //If a new username exists in old config with no pw given, keep the old pw hash
  472. //If a new username is found with new password, hash it and push to credential slice
  473. mergedCredentials := []*dynamicproxy.BasicAuthCredentials{}
  474. for _, credential := range newCredentials {
  475. if credential.Password == "" {
  476. //Check if exists in the old credential files
  477. keepUnchange := false
  478. for _, oldCredEntry := range targetProxy.BasicAuthCredentials {
  479. if oldCredEntry.Username == credential.Username {
  480. //Exists! Reuse the old hash
  481. mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
  482. Username: oldCredEntry.Username,
  483. PasswordHash: oldCredEntry.PasswordHash,
  484. })
  485. keepUnchange = true
  486. }
  487. }
  488. if !keepUnchange {
  489. //This is a new username with no pw given
  490. utils.SendErrorResponse(w, "Access password for "+credential.Username+" is empty!")
  491. return
  492. }
  493. } else {
  494. //This username have given password
  495. mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
  496. Username: credential.Username,
  497. PasswordHash: auth.Hash(credential.Password),
  498. })
  499. }
  500. }
  501. targetProxy.BasicAuthCredentials = mergedCredentials
  502. //Save it to file
  503. SaveReverseProxyConfig(targetProxy)
  504. //Replace runtime configuration
  505. targetProxy.UpdateToRuntime()
  506. utils.SendOK(w)
  507. } else {
  508. http.Error(w, "invalid usage", http.StatusMethodNotAllowed)
  509. }
  510. }
  511. // List, Update or Remove the exception paths for basic auth.
  512. func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
  513. if r.Method != http.MethodGet {
  514. http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
  515. }
  516. ep, err := utils.GetPara(r, "ep")
  517. if err != nil {
  518. utils.SendErrorResponse(w, "Invalid ep given")
  519. return
  520. }
  521. //Load the target proxy object from router
  522. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  523. if err != nil {
  524. utils.SendErrorResponse(w, err.Error())
  525. return
  526. }
  527. //List all the exception paths for this proxy
  528. results := targetProxy.BasicAuthExceptionRules
  529. if results == nil {
  530. //It is a config from a really old version of zoraxy. Overwrite it with empty array
  531. results = []*dynamicproxy.BasicAuthExceptionRule{}
  532. }
  533. js, _ := json.Marshal(results)
  534. utils.SendJSONResponse(w, string(js))
  535. return
  536. }
  537. func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
  538. ep, err := utils.PostPara(r, "ep")
  539. if err != nil {
  540. utils.SendErrorResponse(w, "Invalid ep given")
  541. return
  542. }
  543. matchingPrefix, err := utils.PostPara(r, "prefix")
  544. if err != nil {
  545. utils.SendErrorResponse(w, "Invalid matching prefix given")
  546. return
  547. }
  548. //Load the target proxy object from router
  549. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  550. if err != nil {
  551. utils.SendErrorResponse(w, err.Error())
  552. return
  553. }
  554. //Check if the prefix starts with /. If not, prepend it
  555. if !strings.HasPrefix(matchingPrefix, "/") {
  556. matchingPrefix = "/" + matchingPrefix
  557. }
  558. //Add a new exception rule if it is not already exists
  559. alreadyExists := false
  560. for _, thisExceptionRule := range targetProxy.BasicAuthExceptionRules {
  561. if thisExceptionRule.PathPrefix == matchingPrefix {
  562. alreadyExists = true
  563. break
  564. }
  565. }
  566. if alreadyExists {
  567. utils.SendErrorResponse(w, "This matching path already exists")
  568. return
  569. }
  570. targetProxy.BasicAuthExceptionRules = append(targetProxy.BasicAuthExceptionRules, &dynamicproxy.BasicAuthExceptionRule{
  571. PathPrefix: strings.TrimSpace(matchingPrefix),
  572. })
  573. //Save configs to runtime and file
  574. targetProxy.UpdateToRuntime()
  575. SaveReverseProxyConfig(targetProxy)
  576. utils.SendOK(w)
  577. }
  578. func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
  579. // Delete a rule
  580. ep, err := utils.PostPara(r, "ep")
  581. if err != nil {
  582. utils.SendErrorResponse(w, "Invalid ep given")
  583. return
  584. }
  585. matchingPrefix, err := utils.PostPara(r, "prefix")
  586. if err != nil {
  587. utils.SendErrorResponse(w, "Invalid matching prefix given")
  588. return
  589. }
  590. // Load the target proxy object from router
  591. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  592. if err != nil {
  593. utils.SendErrorResponse(w, err.Error())
  594. return
  595. }
  596. newExceptionRuleList := []*dynamicproxy.BasicAuthExceptionRule{}
  597. matchingExists := false
  598. for _, thisExceptionalRule := range targetProxy.BasicAuthExceptionRules {
  599. if thisExceptionalRule.PathPrefix != matchingPrefix {
  600. newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule)
  601. } else {
  602. matchingExists = true
  603. }
  604. }
  605. if !matchingExists {
  606. utils.SendErrorResponse(w, "target matching rule not exists")
  607. return
  608. }
  609. targetProxy.BasicAuthExceptionRules = newExceptionRuleList
  610. // Save configs to runtime and file
  611. targetProxy.UpdateToRuntime()
  612. SaveReverseProxyConfig(targetProxy)
  613. utils.SendOK(w)
  614. }
  615. // Report the current status of the reverse proxy server
  616. func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
  617. js, _ := json.Marshal(dynamicProxyRouter)
  618. utils.SendJSONResponse(w, string(js))
  619. }
  620. // Toggle a certain rule on and off
  621. func ReverseProxyToggleRuleSet(w http.ResponseWriter, r *http.Request) {
  622. //No need to check for type as root cannot be turned off
  623. ep, err := utils.PostPara(r, "ep")
  624. if err != nil {
  625. utils.SendErrorResponse(w, "invalid ep given")
  626. return
  627. }
  628. targetProxyRule, err := dynamicProxyRouter.LoadProxy(ep)
  629. if err != nil {
  630. utils.SendErrorResponse(w, "invalid endpoint given")
  631. return
  632. }
  633. enableStr, err := utils.PostPara(r, "enable")
  634. if err != nil {
  635. enableStr = "true"
  636. }
  637. //Flip the enable and disabled tag state
  638. ruleDisabled := enableStr == "false"
  639. targetProxyRule.Disabled = ruleDisabled
  640. err = SaveReverseProxyConfig(targetProxyRule)
  641. if err != nil {
  642. utils.SendErrorResponse(w, "unable to save updated rule")
  643. return
  644. }
  645. utils.SendOK(w)
  646. }
  647. func ReverseProxyListDetail(w http.ResponseWriter, r *http.Request) {
  648. eptype, err := utils.PostPara(r, "type") //Support root and host
  649. if err != nil {
  650. utils.SendErrorResponse(w, "type not defined")
  651. return
  652. }
  653. if eptype == "host" {
  654. epname, err := utils.PostPara(r, "epname")
  655. if err != nil {
  656. utils.SendErrorResponse(w, "epname not defined")
  657. return
  658. }
  659. endpointRaw, ok := dynamicProxyRouter.ProxyEndpoints.Load(epname)
  660. if !ok {
  661. utils.SendErrorResponse(w, "proxy rule not found")
  662. return
  663. }
  664. targetEndpoint := dynamicproxy.CopyEndpoint(endpointRaw.(*dynamicproxy.ProxyEndpoint))
  665. js, _ := json.Marshal(targetEndpoint)
  666. utils.SendJSONResponse(w, string(js))
  667. } else if eptype == "root" {
  668. js, _ := json.Marshal(dynamicProxyRouter.Root)
  669. utils.SendJSONResponse(w, string(js))
  670. } else {
  671. utils.SendErrorResponse(w, "Invalid type given")
  672. }
  673. }
  674. func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
  675. eptype, err := utils.PostPara(r, "type") //Support root and host
  676. if err != nil {
  677. utils.SendErrorResponse(w, "type not defined")
  678. return
  679. }
  680. if eptype == "host" {
  681. results := []*dynamicproxy.ProxyEndpoint{}
  682. dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
  683. thisEndpoint := dynamicproxy.CopyEndpoint(value.(*dynamicproxy.ProxyEndpoint))
  684. //Clear the auth passwords before showing to front-end
  685. cleanedCredentials := []*dynamicproxy.BasicAuthCredentials{}
  686. for _, user := range thisEndpoint.BasicAuthCredentials {
  687. cleanedCredentials = append(cleanedCredentials, &dynamicproxy.BasicAuthCredentials{
  688. Username: user.Username,
  689. PasswordHash: "",
  690. })
  691. }
  692. thisEndpoint.BasicAuthCredentials = cleanedCredentials
  693. results = append(results, thisEndpoint)
  694. return true
  695. })
  696. sort.Slice(results, func(i, j int) bool {
  697. return results[i].Domain < results[j].Domain
  698. })
  699. js, _ := json.Marshal(results)
  700. utils.SendJSONResponse(w, string(js))
  701. } else if eptype == "root" {
  702. js, _ := json.Marshal(dynamicProxyRouter.Root)
  703. utils.SendJSONResponse(w, string(js))
  704. } else {
  705. utils.SendErrorResponse(w, "Invalid type given")
  706. }
  707. }
  708. // Handle port 80 incoming traffics
  709. func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) {
  710. enabled, err := utils.GetPara(r, "enable")
  711. if err != nil {
  712. //Load the current status
  713. currentEnabled := false
  714. err = sysdb.Read("settings", "listenP80", &currentEnabled)
  715. if err != nil {
  716. utils.SendErrorResponse(w, err.Error())
  717. return
  718. }
  719. js, _ := json.Marshal(currentEnabled)
  720. utils.SendJSONResponse(w, string(js))
  721. } else {
  722. if enabled == "true" {
  723. sysdb.Write("settings", "listenP80", true)
  724. SystemWideLogger.Println("Enabling port 80 listener")
  725. dynamicProxyRouter.UpdatePort80ListenerState(true)
  726. } else if enabled == "false" {
  727. sysdb.Write("settings", "listenP80", false)
  728. SystemWideLogger.Println("Disabling port 80 listener")
  729. dynamicProxyRouter.UpdatePort80ListenerState(false)
  730. } else {
  731. utils.SendErrorResponse(w, "invalid mode given: "+enabled)
  732. }
  733. utils.SendOK(w)
  734. }
  735. }
  736. // Handle https redirect
  737. func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
  738. useRedirect, err := utils.GetPara(r, "set")
  739. if err != nil {
  740. currentRedirectToHttps := false
  741. //Load the current status
  742. err = sysdb.Read("settings", "redirect", &currentRedirectToHttps)
  743. if err != nil {
  744. utils.SendErrorResponse(w, err.Error())
  745. return
  746. }
  747. js, _ := json.Marshal(currentRedirectToHttps)
  748. utils.SendJSONResponse(w, string(js))
  749. } else {
  750. if dynamicProxyRouter.Option.Port == 80 {
  751. utils.SendErrorResponse(w, "This option is not available when listening on port 80")
  752. return
  753. }
  754. if useRedirect == "true" {
  755. sysdb.Write("settings", "redirect", true)
  756. SystemWideLogger.Println("Updating force HTTPS redirection to true")
  757. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
  758. } else if useRedirect == "false" {
  759. sysdb.Write("settings", "redirect", false)
  760. SystemWideLogger.Println("Updating force HTTPS redirection to false")
  761. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
  762. }
  763. utils.SendOK(w)
  764. }
  765. }
  766. // Handle checking if the current user is accessing via the reverse proxied interface
  767. // Of the management interface.
  768. func HandleManagementProxyCheck(w http.ResponseWriter, r *http.Request) {
  769. isProxied := dynamicProxyRouter.IsProxiedSubdomain(r)
  770. js, _ := json.Marshal(isProxied)
  771. utils.SendJSONResponse(w, string(js))
  772. }
  773. func HandleDevelopmentModeChange(w http.ResponseWriter, r *http.Request) {
  774. enableDevelopmentModeStr, err := utils.GetPara(r, "enable")
  775. if err != nil {
  776. //Load the current development mode toggle state
  777. js, _ := json.Marshal(dynamicProxyRouter.Option.NoCache)
  778. utils.SendJSONResponse(w, string(js))
  779. } else {
  780. //Write changes to runtime
  781. enableDevelopmentMode := false
  782. if enableDevelopmentModeStr == "true" {
  783. enableDevelopmentMode = true
  784. }
  785. //Write changes to runtime
  786. dynamicProxyRouter.Option.NoCache = enableDevelopmentMode
  787. //Write changes to database
  788. sysdb.Write("settings", "devMode", enableDevelopmentMode)
  789. utils.SendOK(w)
  790. }
  791. }
  792. // Handle incoming port set. Change the current proxy incoming port
  793. func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
  794. newIncomingPort, err := utils.PostPara(r, "incoming")
  795. if err != nil {
  796. utils.SendErrorResponse(w, "invalid incoming port given")
  797. return
  798. }
  799. newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
  800. if err != nil {
  801. utils.SendErrorResponse(w, "Invalid incoming port given")
  802. return
  803. }
  804. //Check if it is identical as proxy root (recursion!)
  805. if dynamicProxyRouter.Root == nil || dynamicProxyRouter.Root.Domain == "" {
  806. //Check if proxy root is set before checking recursive listen
  807. //Fixing issue #43
  808. utils.SendErrorResponse(w, "Set Proxy Root before changing inbound port")
  809. return
  810. }
  811. proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/")
  812. if strings.EqualFold(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.EqualFold(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) {
  813. //Listening port is same as proxy root
  814. //Not allow recursive settings
  815. utils.SendErrorResponse(w, "Recursive listening port! Check your proxy root settings.")
  816. return
  817. }
  818. //Stop and change the setting of the reverse proxy service
  819. if dynamicProxyRouter.Running {
  820. dynamicProxyRouter.StopProxyService()
  821. dynamicProxyRouter.Option.Port = newIncomingPortInt
  822. dynamicProxyRouter.StartProxyService()
  823. } else {
  824. //Only change setting but not starting the proxy service
  825. dynamicProxyRouter.Option.Port = newIncomingPortInt
  826. }
  827. sysdb.Write("settings", "inbound", newIncomingPortInt)
  828. utils.SendOK(w)
  829. }
  830. /* Handle Custom Header Rules */
  831. //List all the custom header defined in this proxy rule
  832. func HandleCustomHeaderList(w http.ResponseWriter, r *http.Request) {
  833. epType, err := utils.PostPara(r, "type")
  834. if err != nil {
  835. utils.SendErrorResponse(w, "endpoint type not defined")
  836. return
  837. }
  838. domain, err := utils.PostPara(r, "domain")
  839. if err != nil {
  840. utils.SendErrorResponse(w, "domain or matching rule not defined")
  841. return
  842. }
  843. var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
  844. if epType == "root" {
  845. targetProxyEndpoint = dynamicProxyRouter.Root
  846. } else {
  847. ep, err := dynamicProxyRouter.LoadProxy(domain)
  848. if err != nil {
  849. utils.SendErrorResponse(w, "target endpoint not exists")
  850. return
  851. }
  852. targetProxyEndpoint = ep
  853. }
  854. //List all custom headers
  855. customHeaderList := targetProxyEndpoint.UserDefinedHeaders
  856. if customHeaderList == nil {
  857. customHeaderList = []*dynamicproxy.UserDefinedHeader{}
  858. }
  859. js, _ := json.Marshal(customHeaderList)
  860. utils.SendJSONResponse(w, string(js))
  861. }
  862. // Add a new header to the target endpoint
  863. func HandleCustomHeaderAdd(w http.ResponseWriter, r *http.Request) {
  864. epType, err := utils.PostPara(r, "type")
  865. if err != nil {
  866. utils.SendErrorResponse(w, "endpoint type not defined")
  867. return
  868. }
  869. domain, err := utils.PostPara(r, "domain")
  870. if err != nil {
  871. utils.SendErrorResponse(w, "domain or matching rule not defined")
  872. return
  873. }
  874. name, err := utils.PostPara(r, "name")
  875. if err != nil {
  876. utils.SendErrorResponse(w, "HTTP header name not set")
  877. return
  878. }
  879. value, err := utils.PostPara(r, "value")
  880. if err != nil {
  881. utils.SendErrorResponse(w, "HTTP header value not set")
  882. return
  883. }
  884. var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
  885. if epType == "root" {
  886. targetProxyEndpoint = dynamicProxyRouter.Root
  887. } else {
  888. ep, err := dynamicProxyRouter.LoadProxy(domain)
  889. if err != nil {
  890. utils.SendErrorResponse(w, "target endpoint not exists")
  891. return
  892. }
  893. targetProxyEndpoint = ep
  894. }
  895. //Create a new custom header object
  896. targetProxyEndpoint.AddUserDefinedHeader(name, value)
  897. //Save it (no need reload as header are not handled by dpcore)
  898. err = SaveReverseProxyConfig(targetProxyEndpoint)
  899. if err != nil {
  900. utils.SendErrorResponse(w, "unable to save update")
  901. return
  902. }
  903. utils.SendOK(w)
  904. }
  905. // Remove a header from the target endpoint
  906. func HandleCustomHeaderRemove(w http.ResponseWriter, r *http.Request) {
  907. epType, err := utils.PostPara(r, "type")
  908. if err != nil {
  909. utils.SendErrorResponse(w, "endpoint type not defined")
  910. return
  911. }
  912. domain, err := utils.PostPara(r, "domain")
  913. if err != nil {
  914. utils.SendErrorResponse(w, "domain or matching rule not defined")
  915. return
  916. }
  917. name, err := utils.PostPara(r, "name")
  918. if err != nil {
  919. utils.SendErrorResponse(w, "HTTP header name not set")
  920. return
  921. }
  922. var targetProxyEndpoint *dynamicproxy.ProxyEndpoint
  923. if epType == "root" {
  924. targetProxyEndpoint = dynamicProxyRouter.Root
  925. } else {
  926. ep, err := dynamicProxyRouter.LoadProxy(domain)
  927. if err != nil {
  928. utils.SendErrorResponse(w, "target endpoint not exists")
  929. return
  930. }
  931. targetProxyEndpoint = ep
  932. }
  933. targetProxyEndpoint.RemoveUserDefinedHeader(name)
  934. err = SaveReverseProxyConfig(targetProxyEndpoint)
  935. if err != nil {
  936. utils.SendErrorResponse(w, "unable to save update")
  937. return
  938. }
  939. utils.SendOK(w)
  940. }