reverseproxy.go 26 KB

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