reverseproxy.go 25 KB

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