reverseproxy.go 30 KB

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