reverseproxy.go 26 KB

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