reverseproxy.go 24 KB

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