reverseproxy.go 33 KB

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