reverseproxy.go 30 KB

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