reverseproxy.go 28 KB

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