1
0

reverseproxy.go 25 KB

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