1
0

reverseproxy.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  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. SkipCertValidations: record.SkipTlsValidation,
  85. RequireBasicAuth: record.RequireBasicAuth,
  86. BasicAuthCredentials: record.BasicAuthCredentials,
  87. BasicAuthExceptionRules: record.BasicAuthExceptionRules,
  88. })
  89. } else if record.ProxyType == "vdir" {
  90. dynamicProxyRouter.AddVirtualDirectoryProxyService(&dynamicproxy.VdirOptions{
  91. RootName: record.Rootname,
  92. Domain: record.ProxyTarget,
  93. RequireTLS: record.UseTLS,
  94. SkipCertValidations: record.SkipTlsValidation,
  95. RequireBasicAuth: record.RequireBasicAuth,
  96. BasicAuthCredentials: record.BasicAuthCredentials,
  97. BasicAuthExceptionRules: record.BasicAuthExceptionRules,
  98. })
  99. } else {
  100. log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf))
  101. }
  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. log.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. log.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, vdir and subd
  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. stv, _ := utils.PostPara(r, "tlsval")
  162. if stv == "" {
  163. stv = "false"
  164. }
  165. skipTlsValidation := (stv == "true")
  166. rba, _ := utils.PostPara(r, "bauth")
  167. if rba == "" {
  168. rba = "false"
  169. }
  170. requireBasicAuth := (rba == "true")
  171. //Prase the basic auth to correct structure
  172. cred, _ := utils.PostPara(r, "cred")
  173. basicAuthCredentials := []*dynamicproxy.BasicAuthCredentials{}
  174. if requireBasicAuth {
  175. preProcessCredentials := []*dynamicproxy.BasicAuthUnhashedCredentials{}
  176. err = json.Unmarshal([]byte(cred), &preProcessCredentials)
  177. if err != nil {
  178. utils.SendErrorResponse(w, "invalid user credentials")
  179. return
  180. }
  181. //Check if there are empty password credentials
  182. for _, credObj := range preProcessCredentials {
  183. if strings.TrimSpace(credObj.Password) == "" {
  184. utils.SendErrorResponse(w, credObj.Username+" has empty password")
  185. return
  186. }
  187. }
  188. //Convert and hash the passwords
  189. for _, credObj := range preProcessCredentials {
  190. basicAuthCredentials = append(basicAuthCredentials, &dynamicproxy.BasicAuthCredentials{
  191. Username: credObj.Username,
  192. PasswordHash: auth.Hash(credObj.Password),
  193. })
  194. }
  195. }
  196. rootname := ""
  197. if eptype == "vdir" {
  198. vdir, err := utils.PostPara(r, "rootname")
  199. if err != nil {
  200. utils.SendErrorResponse(w, "vdir not defined")
  201. return
  202. }
  203. //Vdir must start with /
  204. if !strings.HasPrefix(vdir, "/") {
  205. vdir = "/" + vdir
  206. }
  207. rootname = vdir
  208. thisOption := dynamicproxy.VdirOptions{
  209. RootName: vdir,
  210. Domain: endpoint,
  211. RequireTLS: useTLS,
  212. SkipCertValidations: skipTlsValidation,
  213. RequireBasicAuth: requireBasicAuth,
  214. BasicAuthCredentials: basicAuthCredentials,
  215. }
  216. dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
  217. } else if eptype == "subd" {
  218. subdomain, err := utils.PostPara(r, "rootname")
  219. if err != nil {
  220. utils.SendErrorResponse(w, "subdomain not defined")
  221. return
  222. }
  223. rootname = subdomain
  224. thisOption := dynamicproxy.SubdOptions{
  225. MatchingDomain: subdomain,
  226. Domain: endpoint,
  227. RequireTLS: useTLS,
  228. SkipCertValidations: skipTlsValidation,
  229. RequireBasicAuth: requireBasicAuth,
  230. BasicAuthCredentials: basicAuthCredentials,
  231. }
  232. dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
  233. } else if eptype == "root" {
  234. rootname = "root"
  235. thisOption := dynamicproxy.RootOptions{
  236. ProxyLocation: endpoint,
  237. RequireTLS: useTLS,
  238. }
  239. dynamicProxyRouter.SetRootProxy(&thisOption)
  240. } else {
  241. //Invalid eptype
  242. utils.SendErrorResponse(w, "Invalid endpoint type")
  243. return
  244. }
  245. //Save it
  246. thisProxyConfigRecord := Record{
  247. ProxyType: eptype,
  248. Rootname: rootname,
  249. ProxyTarget: endpoint,
  250. UseTLS: useTLS,
  251. SkipTlsValidation: skipTlsValidation,
  252. RequireBasicAuth: requireBasicAuth,
  253. BasicAuthCredentials: basicAuthCredentials,
  254. }
  255. SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
  256. //Update utm if exists
  257. if uptimeMonitor != nil {
  258. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  259. uptimeMonitor.CleanRecords()
  260. }
  261. utils.SendOK(w)
  262. }
  263. /*
  264. ReverseProxyHandleEditEndpoint handles proxy endpoint edit
  265. This endpoint do not handle
  266. basic auth credential update. The credential
  267. will be loaded from old config and reused
  268. */
  269. func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
  270. eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
  271. if err != nil {
  272. utils.SendErrorResponse(w, "type not defined")
  273. return
  274. }
  275. rootNameOrMatchingDomain, err := utils.PostPara(r, "rootname")
  276. if err != nil {
  277. utils.SendErrorResponse(w, "Target proxy rule not defined")
  278. return
  279. }
  280. endpoint, err := utils.PostPara(r, "ep")
  281. if err != nil {
  282. utils.SendErrorResponse(w, "endpoint not defined")
  283. return
  284. }
  285. tls, _ := utils.PostPara(r, "tls")
  286. if tls == "" {
  287. tls = "false"
  288. }
  289. useTLS := (tls == "true")
  290. stv, _ := utils.PostPara(r, "tlsval")
  291. if stv == "" {
  292. stv = "false"
  293. }
  294. skipTlsValidation := (stv == "true")
  295. rba, _ := utils.PostPara(r, "bauth")
  296. if rba == "" {
  297. rba = "false"
  298. }
  299. requireBasicAuth := (rba == "true")
  300. //Load the previous basic auth credentials from current proxy rules
  301. targetProxyEntry, err := dynamicProxyRouter.LoadProxy(eptype, rootNameOrMatchingDomain)
  302. if err != nil {
  303. utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
  304. return
  305. }
  306. if eptype == "vdir" {
  307. thisOption := dynamicproxy.VdirOptions{
  308. RootName: targetProxyEntry.RootOrMatchingDomain,
  309. Domain: endpoint,
  310. RequireTLS: useTLS,
  311. SkipCertValidations: skipTlsValidation,
  312. RequireBasicAuth: requireBasicAuth,
  313. BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
  314. }
  315. targetProxyEntry.Remove()
  316. dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
  317. } else if eptype == "subd" {
  318. thisOption := dynamicproxy.SubdOptions{
  319. MatchingDomain: targetProxyEntry.RootOrMatchingDomain,
  320. Domain: endpoint,
  321. RequireTLS: useTLS,
  322. SkipCertValidations: skipTlsValidation,
  323. RequireBasicAuth: requireBasicAuth,
  324. BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
  325. }
  326. targetProxyEntry.Remove()
  327. dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
  328. }
  329. //Save it to file
  330. thisProxyConfigRecord := Record{
  331. ProxyType: eptype,
  332. Rootname: targetProxyEntry.RootOrMatchingDomain,
  333. ProxyTarget: endpoint,
  334. UseTLS: useTLS,
  335. SkipTlsValidation: skipTlsValidation,
  336. RequireBasicAuth: requireBasicAuth,
  337. BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
  338. }
  339. SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
  340. utils.SendOK(w)
  341. }
  342. func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
  343. ep, err := utils.GetPara(r, "ep")
  344. if err != nil {
  345. utils.SendErrorResponse(w, "Invalid ep given")
  346. return
  347. }
  348. ptype, err := utils.PostPara(r, "ptype")
  349. if err != nil {
  350. utils.SendErrorResponse(w, "Invalid ptype given")
  351. return
  352. }
  353. //Remove the config from runtime
  354. err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ptype, ep)
  355. if err != nil {
  356. utils.SendErrorResponse(w, err.Error())
  357. return
  358. }
  359. //Remove the config from file
  360. RemoveReverseProxyConfigFile(ep)
  361. //Update utm if exists
  362. if uptimeMonitor != nil {
  363. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  364. uptimeMonitor.CleanRecords()
  365. }
  366. utils.SendOK(w)
  367. }
  368. /*
  369. Handle update request for basic auth credential
  370. Require paramter: ep (Endpoint) and pytype (proxy Type)
  371. if request with GET, the handler will return current credentials
  372. on this endpoint by its username
  373. if request is POST, the handler will write the results to proxy config
  374. */
  375. func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
  376. if r.Method == http.MethodGet {
  377. ep, err := utils.GetPara(r, "ep")
  378. if err != nil {
  379. utils.SendErrorResponse(w, "Invalid ep given")
  380. return
  381. }
  382. ptype, err := utils.GetPara(r, "ptype")
  383. if err != nil {
  384. utils.SendErrorResponse(w, "Invalid ptype given")
  385. return
  386. }
  387. //Load the target proxy object from router
  388. targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
  389. if err != nil {
  390. utils.SendErrorResponse(w, err.Error())
  391. return
  392. }
  393. usernames := []string{}
  394. for _, cred := range targetProxy.BasicAuthCredentials {
  395. usernames = append(usernames, cred.Username)
  396. }
  397. js, _ := json.Marshal(usernames)
  398. utils.SendJSONResponse(w, string(js))
  399. } else if r.Method == http.MethodPost {
  400. //Write to target
  401. ep, err := utils.PostPara(r, "ep")
  402. if err != nil {
  403. utils.SendErrorResponse(w, "Invalid ep given")
  404. return
  405. }
  406. ptype, err := utils.PostPara(r, "ptype")
  407. if err != nil {
  408. utils.SendErrorResponse(w, "Invalid ptype given")
  409. return
  410. }
  411. if ptype != "vdir" && ptype != "subd" {
  412. utils.SendErrorResponse(w, "Invalid ptype given")
  413. return
  414. }
  415. creds, err := utils.PostPara(r, "creds")
  416. if err != nil {
  417. utils.SendErrorResponse(w, "Invalid ptype given")
  418. return
  419. }
  420. //Load the target proxy object from router
  421. targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
  422. if err != nil {
  423. utils.SendErrorResponse(w, err.Error())
  424. return
  425. }
  426. //Try to marshal the content of creds into the suitable structure
  427. newCredentials := []*dynamicproxy.BasicAuthUnhashedCredentials{}
  428. err = json.Unmarshal([]byte(creds), &newCredentials)
  429. if err != nil {
  430. utils.SendErrorResponse(w, "Malformed credential data")
  431. return
  432. }
  433. //Merge the credentials into the original config
  434. //If a new username exists in old config with no pw given, keep the old pw hash
  435. //If a new username is found with new password, hash it and push to credential slice
  436. mergedCredentials := []*dynamicproxy.BasicAuthCredentials{}
  437. for _, credential := range newCredentials {
  438. if credential.Password == "" {
  439. //Check if exists in the old credential files
  440. keepUnchange := false
  441. for _, oldCredEntry := range targetProxy.BasicAuthCredentials {
  442. if oldCredEntry.Username == credential.Username {
  443. //Exists! Reuse the old hash
  444. mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
  445. Username: oldCredEntry.Username,
  446. PasswordHash: oldCredEntry.PasswordHash,
  447. })
  448. keepUnchange = true
  449. }
  450. }
  451. if !keepUnchange {
  452. //This is a new username with no pw given
  453. utils.SendErrorResponse(w, "Access password for "+credential.Username+" is empty!")
  454. return
  455. }
  456. } else {
  457. //This username have given password
  458. mergedCredentials = append(mergedCredentials, &dynamicproxy.BasicAuthCredentials{
  459. Username: credential.Username,
  460. PasswordHash: auth.Hash(credential.Password),
  461. })
  462. }
  463. }
  464. targetProxy.BasicAuthCredentials = mergedCredentials
  465. //Save it to file
  466. SaveReverseProxyEndpointToFile(targetProxy)
  467. //Replace runtime configuration
  468. targetProxy.UpdateToRuntime()
  469. utils.SendOK(w)
  470. } else {
  471. http.Error(w, "invalid usage", http.StatusMethodNotAllowed)
  472. }
  473. }
  474. // List, Update or Remove the exception paths for basic auth.
  475. func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
  476. if r.Method != http.MethodGet {
  477. http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
  478. }
  479. ep, err := utils.GetPara(r, "ep")
  480. if err != nil {
  481. utils.SendErrorResponse(w, "Invalid ep given")
  482. return
  483. }
  484. ptype, err := utils.GetPara(r, "ptype")
  485. if err != nil {
  486. utils.SendErrorResponse(w, "Invalid ptype given")
  487. return
  488. }
  489. //Load the target proxy object from router
  490. targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
  491. if err != nil {
  492. utils.SendErrorResponse(w, err.Error())
  493. return
  494. }
  495. //List all the exception paths for this proxy
  496. results := targetProxy.BasicAuthExceptionRules
  497. if results == nil {
  498. //It is a config from a really old version of zoraxy. Overwrite it with empty array
  499. results = []*dynamicproxy.BasicAuthExceptionRule{}
  500. }
  501. js, _ := json.Marshal(results)
  502. utils.SendJSONResponse(w, string(js))
  503. return
  504. }
  505. func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
  506. ep, err := utils.PostPara(r, "ep")
  507. if err != nil {
  508. utils.SendErrorResponse(w, "Invalid ep given")
  509. return
  510. }
  511. ptype, err := utils.PostPara(r, "ptype")
  512. if err != nil {
  513. utils.SendErrorResponse(w, "Invalid ptype given")
  514. return
  515. }
  516. matchingPrefix, err := utils.PostPara(r, "prefix")
  517. if err != nil {
  518. utils.SendErrorResponse(w, "Invalid matching prefix given")
  519. return
  520. }
  521. //Load the target proxy object from router
  522. targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
  523. if err != nil {
  524. utils.SendErrorResponse(w, err.Error())
  525. return
  526. }
  527. //Check if the prefix starts with /. If not, prepend it
  528. if !strings.HasPrefix(matchingPrefix, "/") {
  529. matchingPrefix = "/" + matchingPrefix
  530. }
  531. //Add a new exception rule if it is not already exists
  532. alreadyExists := false
  533. for _, thisExceptionRule := range targetProxy.BasicAuthExceptionRules {
  534. if thisExceptionRule.PathPrefix == matchingPrefix {
  535. alreadyExists = true
  536. break
  537. }
  538. }
  539. if alreadyExists {
  540. utils.SendErrorResponse(w, "This matching path already exists")
  541. return
  542. }
  543. targetProxy.BasicAuthExceptionRules = append(targetProxy.BasicAuthExceptionRules, &dynamicproxy.BasicAuthExceptionRule{
  544. PathPrefix: strings.TrimSpace(matchingPrefix),
  545. })
  546. //Save configs to runtime and file
  547. targetProxy.UpdateToRuntime()
  548. SaveReverseProxyEndpointToFile(targetProxy)
  549. utils.SendOK(w)
  550. }
  551. func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
  552. // Delete a rule
  553. ep, err := utils.PostPara(r, "ep")
  554. if err != nil {
  555. utils.SendErrorResponse(w, "Invalid ep given")
  556. return
  557. }
  558. ptype, err := utils.PostPara(r, "ptype")
  559. if err != nil {
  560. utils.SendErrorResponse(w, "Invalid ptype given")
  561. return
  562. }
  563. matchingPrefix, err := utils.PostPara(r, "prefix")
  564. if err != nil {
  565. utils.SendErrorResponse(w, "Invalid matching prefix given")
  566. return
  567. }
  568. // Load the target proxy object from router
  569. targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
  570. if err != nil {
  571. utils.SendErrorResponse(w, err.Error())
  572. return
  573. }
  574. newExceptionRuleList := []*dynamicproxy.BasicAuthExceptionRule{}
  575. matchingExists := false
  576. for _, thisExceptionalRule := range targetProxy.BasicAuthExceptionRules {
  577. if thisExceptionalRule.PathPrefix != matchingPrefix {
  578. newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule)
  579. } else {
  580. matchingExists = true
  581. }
  582. }
  583. if !matchingExists {
  584. utils.SendErrorResponse(w, "target matching rule not exists")
  585. return
  586. }
  587. targetProxy.BasicAuthExceptionRules = newExceptionRuleList
  588. // Save configs to runtime and file
  589. targetProxy.UpdateToRuntime()
  590. SaveReverseProxyEndpointToFile(targetProxy)
  591. utils.SendOK(w)
  592. }
  593. func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
  594. js, _ := json.Marshal(dynamicProxyRouter)
  595. utils.SendJSONResponse(w, string(js))
  596. }
  597. func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
  598. eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
  599. if err != nil {
  600. utils.SendErrorResponse(w, "type not defined")
  601. return
  602. }
  603. if eptype == "vdir" {
  604. results := []*dynamicproxy.ProxyEndpoint{}
  605. dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
  606. results = append(results, value.(*dynamicproxy.ProxyEndpoint))
  607. return true
  608. })
  609. sort.Slice(results, func(i, j int) bool {
  610. return results[i].Domain < results[j].Domain
  611. })
  612. js, _ := json.Marshal(results)
  613. utils.SendJSONResponse(w, string(js))
  614. } else if eptype == "subd" {
  615. results := []*dynamicproxy.ProxyEndpoint{}
  616. dynamicProxyRouter.SubdomainEndpoint.Range(func(key, value interface{}) bool {
  617. results = append(results, value.(*dynamicproxy.ProxyEndpoint))
  618. return true
  619. })
  620. sort.Slice(results, func(i, j int) bool {
  621. return results[i].RootOrMatchingDomain < results[j].RootOrMatchingDomain
  622. })
  623. js, _ := json.Marshal(results)
  624. utils.SendJSONResponse(w, string(js))
  625. } else if eptype == "root" {
  626. js, _ := json.Marshal(dynamicProxyRouter.Root)
  627. utils.SendJSONResponse(w, string(js))
  628. } else {
  629. utils.SendErrorResponse(w, "Invalid type given")
  630. }
  631. }
  632. // Handle https redirect
  633. func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
  634. useRedirect, err := utils.GetPara(r, "set")
  635. if err != nil {
  636. currentRedirectToHttps := false
  637. //Load the current status
  638. err = sysdb.Read("settings", "redirect", &currentRedirectToHttps)
  639. if err != nil {
  640. utils.SendErrorResponse(w, err.Error())
  641. return
  642. }
  643. js, _ := json.Marshal(currentRedirectToHttps)
  644. utils.SendJSONResponse(w, string(js))
  645. } else {
  646. if dynamicProxyRouter.Option.Port == 80 {
  647. utils.SendErrorResponse(w, "This option is not available when listening on port 80")
  648. return
  649. }
  650. if useRedirect == "true" {
  651. sysdb.Write("settings", "redirect", true)
  652. log.Println("Updating force HTTPS redirection to true")
  653. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
  654. } else if useRedirect == "false" {
  655. sysdb.Write("settings", "redirect", false)
  656. log.Println("Updating force HTTPS redirection to false")
  657. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
  658. }
  659. utils.SendOK(w)
  660. }
  661. }
  662. // Handle checking if the current user is accessing via the reverse proxied interface
  663. // Of the management interface.
  664. func HandleManagementProxyCheck(w http.ResponseWriter, r *http.Request) {
  665. isProxied := dynamicProxyRouter.IsProxiedSubdomain(r)
  666. js, _ := json.Marshal(isProxied)
  667. utils.SendJSONResponse(w, string(js))
  668. }
  669. // Handle incoming port set. Change the current proxy incoming port
  670. func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
  671. newIncomingPort, err := utils.PostPara(r, "incoming")
  672. if err != nil {
  673. utils.SendErrorResponse(w, "invalid incoming port given")
  674. return
  675. }
  676. newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
  677. if err != nil {
  678. utils.SendErrorResponse(w, "Invalid incoming port given")
  679. return
  680. }
  681. //Check if it is identical as proxy root (recursion!)
  682. if dynamicProxyRouter.Root == nil || dynamicProxyRouter.Root.Domain == "" {
  683. //Check if proxy root is set before checking recursive listen
  684. //Fixing issue #43
  685. utils.SendErrorResponse(w, "Set Proxy Root before changing inbound port")
  686. return
  687. }
  688. proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/")
  689. if strings.HasPrefix(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.HasPrefix(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) {
  690. //Listening port is same as proxy root
  691. //Not allow recursive settings
  692. utils.SendErrorResponse(w, "Recursive listening port! Check your proxy root settings.")
  693. return
  694. }
  695. //Stop and change the setting of the reverse proxy service
  696. if dynamicProxyRouter.Running {
  697. dynamicProxyRouter.StopProxyService()
  698. dynamicProxyRouter.Option.Port = newIncomingPortInt
  699. dynamicProxyRouter.StartProxyService()
  700. } else {
  701. //Only change setting but not starting the proxy service
  702. dynamicProxyRouter.Option.Port = newIncomingPortInt
  703. }
  704. sysdb.Write("settings", "inbound", newIncomingPortInt)
  705. utils.SendOK(w)
  706. }
  707. // Handle list of root route options
  708. func HandleRootRouteOptionList(w http.ResponseWriter, r *http.Request) {
  709. js, _ := json.Marshal(dynamicProxyRouter.RootRoutingOptions)
  710. utils.SendJSONResponse(w, string(js))
  711. }
  712. // Handle update of the root route edge case options. See dynamicproxy/rootRoute.go
  713. func HandleRootRouteOptionsUpdate(w http.ResponseWriter, r *http.Request) {
  714. enableUnsetSubdomainRedirect, err := utils.PostBool(r, "unsetRedirect")
  715. if err != nil {
  716. utils.SendErrorResponse(w, err.Error())
  717. return
  718. }
  719. unsetRedirectTarget, _ := utils.PostPara(r, "unsetRedirectTarget")
  720. newRootOption := dynamicproxy.RootRoutingOptions{
  721. EnableRedirectForUnsetRules: enableUnsetSubdomainRedirect,
  722. UnsetRuleRedirectTarget: unsetRedirectTarget,
  723. }
  724. dynamicProxyRouter.RootRoutingOptions = &newRootOption
  725. err = newRootOption.SaveToFile()
  726. if err != nil {
  727. utils.SendErrorResponse(w, err.Error())
  728. return
  729. }
  730. utils.SendOK(w)
  731. }