reverseproxy.go 36 KB

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