reverseproxy.go 24 KB

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