reverseproxy.go 26 KB

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