1
0

reverseproxy.go 24 KB

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