reverseproxy.go 27 KB

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