reverseproxy.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  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. //Custom headers
  229. UserDefinedHeaders: []*dynamicproxy.UserDefinedHeader{},
  230. //Auth
  231. RequireBasicAuth: requireBasicAuth,
  232. BasicAuthCredentials: basicAuthCredentials,
  233. BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
  234. DefaultSiteOption: 0,
  235. DefaultSiteValue: "",
  236. }
  237. preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint)
  238. if err != nil {
  239. utils.SendErrorResponse(w, "unable to prepare proxy route to target endpoint: "+err.Error())
  240. return
  241. }
  242. dynamicProxyRouter.AddProxyRouteToRuntime(preparedEndpoint)
  243. proxyEndpointCreated = &thisProxyEndpoint
  244. } else if eptype == "root" {
  245. //Get the default site options and target
  246. dsOptString, err := utils.PostPara(r, "defaultSiteOpt")
  247. if err != nil {
  248. utils.SendErrorResponse(w, "default site action not defined")
  249. return
  250. }
  251. var defaultSiteOption int = 1
  252. opt, err := strconv.Atoi(dsOptString)
  253. if err != nil {
  254. utils.SendErrorResponse(w, "invalid default site option")
  255. return
  256. }
  257. defaultSiteOption = opt
  258. dsVal, err := utils.PostPara(r, "defaultSiteVal")
  259. if err != nil && (defaultSiteOption == 1 || defaultSiteOption == 2) {
  260. //Reverse proxy or redirect, must require value to be set
  261. utils.SendErrorResponse(w, "target not defined")
  262. return
  263. }
  264. //Write the root options to file
  265. rootRoutingEndpoint := dynamicproxy.ProxyEndpoint{
  266. ProxyType: dynamicproxy.ProxyType_Root,
  267. RootOrMatchingDomain: "/",
  268. Domain: endpoint,
  269. RequireTLS: useTLS,
  270. BypassGlobalTLS: false,
  271. SkipCertValidations: false,
  272. DefaultSiteOption: defaultSiteOption,
  273. DefaultSiteValue: dsVal,
  274. }
  275. preparedRootProxyRoute, err := dynamicProxyRouter.PrepareProxyRoute(&rootRoutingEndpoint)
  276. if err != nil {
  277. utils.SendErrorResponse(w, "unable to prepare root routing: "+err.Error())
  278. return
  279. }
  280. dynamicProxyRouter.SetProxyRouteAsRoot(preparedRootProxyRoute)
  281. proxyEndpointCreated = &rootRoutingEndpoint
  282. } else {
  283. //Invalid eptype
  284. utils.SendErrorResponse(w, "invalid endpoint type")
  285. return
  286. }
  287. //Save the config to file
  288. err = SaveReverseProxyConfig(proxyEndpointCreated)
  289. if err != nil {
  290. SystemWideLogger.PrintAndLog("Proxy", "Unable to save new proxy rule to file", err)
  291. return
  292. }
  293. //Update utm if exists
  294. UpdateUptimeMonitorTargets()
  295. utils.SendOK(w)
  296. }
  297. /*
  298. ReverseProxyHandleEditEndpoint handles proxy endpoint edit
  299. (host only, for root use Default Site page to edit)
  300. This endpoint do not handle basic auth credential update.
  301. The credential will be loaded from old config and reused
  302. */
  303. func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
  304. rootNameOrMatchingDomain, err := utils.PostPara(r, "rootname")
  305. if err != nil {
  306. utils.SendErrorResponse(w, "Target proxy rule not defined")
  307. return
  308. }
  309. endpoint, err := utils.PostPara(r, "ep")
  310. if err != nil {
  311. utils.SendErrorResponse(w, "endpoint not defined")
  312. return
  313. }
  314. tls, _ := utils.PostPara(r, "tls")
  315. if tls == "" {
  316. tls = "false"
  317. }
  318. useTLS := (tls == "true")
  319. stv, _ := utils.PostPara(r, "tlsval")
  320. if stv == "" {
  321. stv = "false"
  322. }
  323. skipTlsValidation := (stv == "true")
  324. //Load bypass TLS option
  325. bpgtls, _ := utils.PostPara(r, "bpgtls")
  326. if bpgtls == "" {
  327. bpgtls = "false"
  328. }
  329. bypassGlobalTLS := (bpgtls == "true")
  330. rba, _ := utils.PostPara(r, "bauth")
  331. if rba == "" {
  332. rba = "false"
  333. }
  334. requireBasicAuth := (rba == "true")
  335. //Load the previous basic auth credentials from current proxy rules
  336. targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
  337. if err != nil {
  338. utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
  339. return
  340. }
  341. //Generate a new proxyEndpoint from the new config
  342. newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
  343. newProxyEndpoint.Domain = endpoint
  344. newProxyEndpoint.RequireTLS = useTLS
  345. newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS
  346. newProxyEndpoint.SkipCertValidations = skipTlsValidation
  347. newProxyEndpoint.RequireBasicAuth = requireBasicAuth
  348. //Prepare to replace the current routing rule
  349. readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
  350. if err != nil {
  351. utils.SendErrorResponse(w, err.Error())
  352. return
  353. }
  354. targetProxyEntry.Remove()
  355. dynamicProxyRouter.AddProxyRouteToRuntime(readyRoutingRule)
  356. //Save it to file
  357. SaveReverseProxyConfig(newProxyEndpoint)
  358. //Update uptime monitor
  359. UpdateUptimeMonitorTargets()
  360. utils.SendOK(w)
  361. }
  362. func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
  363. ep, err := utils.GetPara(r, "ep")
  364. if err != nil {
  365. utils.SendErrorResponse(w, "Invalid ep given")
  366. return
  367. }
  368. //Remove the config from runtime
  369. err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ep)
  370. if err != nil {
  371. utils.SendErrorResponse(w, err.Error())
  372. return
  373. }
  374. //Remove the config from file
  375. err = RemoveReverseProxyConfig(ep)
  376. if err != nil {
  377. utils.SendErrorResponse(w, err.Error())
  378. return
  379. }
  380. //Update utm if exists
  381. if uptimeMonitor != nil {
  382. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  383. uptimeMonitor.CleanRecords()
  384. }
  385. //Update uptime monitor
  386. UpdateUptimeMonitorTargets()
  387. utils.SendOK(w)
  388. }
  389. /*
  390. Handle update request for basic auth credential
  391. Require paramter: ep (Endpoint) and pytype (proxy Type)
  392. if request with GET, the handler will return current credentials
  393. on this endpoint by its username
  394. if request is POST, the handler will write the results to proxy config
  395. */
  396. func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
  397. if r.Method == http.MethodGet {
  398. ep, err := utils.GetPara(r, "ep")
  399. if err != nil {
  400. utils.SendErrorResponse(w, "Invalid ep given")
  401. return
  402. }
  403. //Load the target proxy object from router
  404. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  405. if err != nil {
  406. utils.SendErrorResponse(w, err.Error())
  407. return
  408. }
  409. usernames := []string{}
  410. for _, cred := range targetProxy.BasicAuthCredentials {
  411. usernames = append(usernames, cred.Username)
  412. }
  413. js, _ := json.Marshal(usernames)
  414. utils.SendJSONResponse(w, string(js))
  415. } else if r.Method == http.MethodPost {
  416. //Write to target
  417. ep, err := utils.PostPara(r, "ep")
  418. if err != nil {
  419. utils.SendErrorResponse(w, "Invalid ep given")
  420. return
  421. }
  422. creds, err := utils.PostPara(r, "creds")
  423. if err != nil {
  424. utils.SendErrorResponse(w, "Invalid ptype given")
  425. return
  426. }
  427. //Load the target proxy object from router
  428. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  429. if err != nil {
  430. utils.SendErrorResponse(w, err.Error())
  431. return
  432. }
  433. //Try to marshal the content of creds into the suitable structure
  434. newCredentials := []*dynamicproxy.BasicAuthUnhashedCredentials{}
  435. err = json.Unmarshal([]byte(creds), &newCredentials)
  436. if err != nil {
  437. utils.SendErrorResponse(w, "Malformed credential data")
  438. return
  439. }
  440. //Merge the credentials into the original config
  441. //If a new username exists in old config with no pw given, keep the old pw hash
  442. //If a new username is found with new password, hash it and push to credential slice
  443. mergedCredentials := []*dynamicproxy.BasicAuthCredentials{}
  444. for _, credential := range newCredentials {
  445. if credential.Password == "" {
  446. //Check if exists in the old credential files
  447. keepUnchange := false
  448. for _, oldCredEntry := range targetProxy.BasicAuthCredentials {
  449. if oldCredEntry.Username == credential.Username {
  450. //Exists! Reuse the old hash
  451. mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
  452. Username: oldCredEntry.Username,
  453. PasswordHash: oldCredEntry.PasswordHash,
  454. })
  455. keepUnchange = true
  456. }
  457. }
  458. if !keepUnchange {
  459. //This is a new username with no pw given
  460. utils.SendErrorResponse(w, "Access password for "+credential.Username+" is empty!")
  461. return
  462. }
  463. } else {
  464. //This username have given password
  465. mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
  466. Username: credential.Username,
  467. PasswordHash: auth.Hash(credential.Password),
  468. })
  469. }
  470. }
  471. targetProxy.BasicAuthCredentials = mergedCredentials
  472. //Save it to file
  473. SaveReverseProxyConfig(targetProxy)
  474. //Replace runtime configuration
  475. targetProxy.UpdateToRuntime()
  476. utils.SendOK(w)
  477. } else {
  478. http.Error(w, "invalid usage", http.StatusMethodNotAllowed)
  479. }
  480. }
  481. // List, Update or Remove the exception paths for basic auth.
  482. func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
  483. if r.Method != http.MethodGet {
  484. http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
  485. }
  486. ep, err := utils.GetPara(r, "ep")
  487. if err != nil {
  488. utils.SendErrorResponse(w, "Invalid ep given")
  489. return
  490. }
  491. //Load the target proxy object from router
  492. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  493. if err != nil {
  494. utils.SendErrorResponse(w, err.Error())
  495. return
  496. }
  497. //List all the exception paths for this proxy
  498. results := targetProxy.BasicAuthExceptionRules
  499. if results == nil {
  500. //It is a config from a really old version of zoraxy. Overwrite it with empty array
  501. results = []*dynamicproxy.BasicAuthExceptionRule{}
  502. }
  503. js, _ := json.Marshal(results)
  504. utils.SendJSONResponse(w, string(js))
  505. return
  506. }
  507. func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
  508. ep, err := utils.PostPara(r, "ep")
  509. if err != nil {
  510. utils.SendErrorResponse(w, "Invalid ep given")
  511. return
  512. }
  513. matchingPrefix, err := utils.PostPara(r, "prefix")
  514. if err != nil {
  515. utils.SendErrorResponse(w, "Invalid matching prefix given")
  516. return
  517. }
  518. //Load the target proxy object from router
  519. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  520. if err != nil {
  521. utils.SendErrorResponse(w, err.Error())
  522. return
  523. }
  524. //Check if the prefix starts with /. If not, prepend it
  525. if !strings.HasPrefix(matchingPrefix, "/") {
  526. matchingPrefix = "/" + matchingPrefix
  527. }
  528. //Add a new exception rule if it is not already exists
  529. alreadyExists := false
  530. for _, thisExceptionRule := range targetProxy.BasicAuthExceptionRules {
  531. if thisExceptionRule.PathPrefix == matchingPrefix {
  532. alreadyExists = true
  533. break
  534. }
  535. }
  536. if alreadyExists {
  537. utils.SendErrorResponse(w, "This matching path already exists")
  538. return
  539. }
  540. targetProxy.BasicAuthExceptionRules = append(targetProxy.BasicAuthExceptionRules, &dynamicproxy.BasicAuthExceptionRule{
  541. PathPrefix: strings.TrimSpace(matchingPrefix),
  542. })
  543. //Save configs to runtime and file
  544. targetProxy.UpdateToRuntime()
  545. SaveReverseProxyConfig(targetProxy)
  546. utils.SendOK(w)
  547. }
  548. func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
  549. // Delete a rule
  550. ep, err := utils.PostPara(r, "ep")
  551. if err != nil {
  552. utils.SendErrorResponse(w, "Invalid ep given")
  553. return
  554. }
  555. matchingPrefix, err := utils.PostPara(r, "prefix")
  556. if err != nil {
  557. utils.SendErrorResponse(w, "Invalid matching prefix given")
  558. return
  559. }
  560. // Load the target proxy object from router
  561. targetProxy, err := dynamicProxyRouter.LoadProxy(ep)
  562. if err != nil {
  563. utils.SendErrorResponse(w, err.Error())
  564. return
  565. }
  566. newExceptionRuleList := []*dynamicproxy.BasicAuthExceptionRule{}
  567. matchingExists := false
  568. for _, thisExceptionalRule := range targetProxy.BasicAuthExceptionRules {
  569. if thisExceptionalRule.PathPrefix != matchingPrefix {
  570. newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule)
  571. } else {
  572. matchingExists = true
  573. }
  574. }
  575. if !matchingExists {
  576. utils.SendErrorResponse(w, "target matching rule not exists")
  577. return
  578. }
  579. targetProxy.BasicAuthExceptionRules = newExceptionRuleList
  580. // Save configs to runtime and file
  581. targetProxy.UpdateToRuntime()
  582. SaveReverseProxyConfig(targetProxy)
  583. utils.SendOK(w)
  584. }
  585. func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
  586. js, _ := json.Marshal(dynamicProxyRouter)
  587. utils.SendJSONResponse(w, string(js))
  588. }
  589. func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
  590. eptype, err := utils.PostPara(r, "type") //Support root and host
  591. if err != nil {
  592. utils.SendErrorResponse(w, "type not defined")
  593. return
  594. }
  595. if eptype == "host" {
  596. results := []*dynamicproxy.ProxyEndpoint{}
  597. dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
  598. thisEndpoint := dynamicproxy.CopyEndpoint(value.(*dynamicproxy.ProxyEndpoint))
  599. //Clear the auth passwords before showing to front-end
  600. cleanedCredentials := []*dynamicproxy.BasicAuthCredentials{}
  601. for _, user := range thisEndpoint.BasicAuthCredentials {
  602. cleanedCredentials = append(cleanedCredentials, &dynamicproxy.BasicAuthCredentials{
  603. Username: user.Username,
  604. PasswordHash: "",
  605. })
  606. }
  607. thisEndpoint.BasicAuthCredentials = cleanedCredentials
  608. results = append(results, thisEndpoint)
  609. return true
  610. })
  611. sort.Slice(results, func(i, j int) bool {
  612. return results[i].Domain < results[j].Domain
  613. })
  614. js, _ := json.Marshal(results)
  615. utils.SendJSONResponse(w, string(js))
  616. } else if eptype == "root" {
  617. js, _ := json.Marshal(dynamicProxyRouter.Root)
  618. utils.SendJSONResponse(w, string(js))
  619. } else {
  620. utils.SendErrorResponse(w, "Invalid type given")
  621. }
  622. }
  623. // Handle port 80 incoming traffics
  624. func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) {
  625. enabled, err := utils.GetPara(r, "enable")
  626. if err != nil {
  627. //Load the current status
  628. currentEnabled := false
  629. err = sysdb.Read("settings", "listenP80", &currentEnabled)
  630. if err != nil {
  631. utils.SendErrorResponse(w, err.Error())
  632. return
  633. }
  634. js, _ := json.Marshal(currentEnabled)
  635. utils.SendJSONResponse(w, string(js))
  636. } else {
  637. if enabled == "true" {
  638. sysdb.Write("settings", "listenP80", true)
  639. SystemWideLogger.Println("Enabling port 80 listener")
  640. dynamicProxyRouter.UpdatePort80ListenerState(true)
  641. } else if enabled == "false" {
  642. sysdb.Write("settings", "listenP80", false)
  643. SystemWideLogger.Println("Disabling port 80 listener")
  644. dynamicProxyRouter.UpdatePort80ListenerState(false)
  645. } else {
  646. utils.SendErrorResponse(w, "invalid mode given: "+enabled)
  647. }
  648. utils.SendOK(w)
  649. }
  650. }
  651. // Handle https redirect
  652. func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
  653. useRedirect, err := utils.GetPara(r, "set")
  654. if err != nil {
  655. currentRedirectToHttps := false
  656. //Load the current status
  657. err = sysdb.Read("settings", "redirect", &currentRedirectToHttps)
  658. if err != nil {
  659. utils.SendErrorResponse(w, err.Error())
  660. return
  661. }
  662. js, _ := json.Marshal(currentRedirectToHttps)
  663. utils.SendJSONResponse(w, string(js))
  664. } else {
  665. if dynamicProxyRouter.Option.Port == 80 {
  666. utils.SendErrorResponse(w, "This option is not available when listening on port 80")
  667. return
  668. }
  669. if useRedirect == "true" {
  670. sysdb.Write("settings", "redirect", true)
  671. SystemWideLogger.Println("Updating force HTTPS redirection to true")
  672. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
  673. } else if useRedirect == "false" {
  674. sysdb.Write("settings", "redirect", false)
  675. SystemWideLogger.Println("Updating force HTTPS redirection to false")
  676. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
  677. }
  678. utils.SendOK(w)
  679. }
  680. }
  681. // Handle checking if the current user is accessing via the reverse proxied interface
  682. // Of the management interface.
  683. func HandleManagementProxyCheck(w http.ResponseWriter, r *http.Request) {
  684. isProxied := dynamicProxyRouter.IsProxiedSubdomain(r)
  685. js, _ := json.Marshal(isProxied)
  686. utils.SendJSONResponse(w, string(js))
  687. }
  688. func HandleDevelopmentModeChange(w http.ResponseWriter, r *http.Request) {
  689. enableDevelopmentModeStr, err := utils.GetPara(r, "enable")
  690. if err != nil {
  691. //Load the current development mode toggle state
  692. js, _ := json.Marshal(dynamicProxyRouter.Option.NoCache)
  693. utils.SendJSONResponse(w, string(js))
  694. } else {
  695. //Write changes to runtime
  696. enableDevelopmentMode := false
  697. if enableDevelopmentModeStr == "true" {
  698. enableDevelopmentMode = true
  699. }
  700. //Write changes to runtime
  701. dynamicProxyRouter.Option.NoCache = enableDevelopmentMode
  702. //Write changes to database
  703. sysdb.Write("settings", "devMode", enableDevelopmentMode)
  704. utils.SendOK(w)
  705. }
  706. }
  707. // Handle incoming port set. Change the current proxy incoming port
  708. func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
  709. newIncomingPort, err := utils.PostPara(r, "incoming")
  710. if err != nil {
  711. utils.SendErrorResponse(w, "invalid incoming port given")
  712. return
  713. }
  714. newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
  715. if err != nil {
  716. utils.SendErrorResponse(w, "Invalid incoming port given")
  717. return
  718. }
  719. //Check if it is identical as proxy root (recursion!)
  720. if dynamicProxyRouter.Root == nil || dynamicProxyRouter.Root.Domain == "" {
  721. //Check if proxy root is set before checking recursive listen
  722. //Fixing issue #43
  723. utils.SendErrorResponse(w, "Set Proxy Root before changing inbound port")
  724. return
  725. }
  726. proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/")
  727. if strings.HasPrefix(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.HasPrefix(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) {
  728. //Listening port is same as proxy root
  729. //Not allow recursive settings
  730. utils.SendErrorResponse(w, "Recursive listening port! Check your proxy root settings.")
  731. return
  732. }
  733. //Stop and change the setting of the reverse proxy service
  734. if dynamicProxyRouter.Running {
  735. dynamicProxyRouter.StopProxyService()
  736. dynamicProxyRouter.Option.Port = newIncomingPortInt
  737. dynamicProxyRouter.StartProxyService()
  738. } else {
  739. //Only change setting but not starting the proxy service
  740. dynamicProxyRouter.Option.Port = newIncomingPortInt
  741. }
  742. sysdb.Write("settings", "inbound", newIncomingPortInt)
  743. utils.SendOK(w)
  744. }