reverseproxy.go 36 KB

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