1
0

reverseproxy.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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. forceHttpsRedirect := false
  36. sysdb.Read("settings", "redirect", &forceHttpsRedirect)
  37. if forceHttpsRedirect {
  38. log.Println("Force HTTPS mode enabled")
  39. } else {
  40. log.Println("Force HTTPS mode disabled")
  41. }
  42. dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
  43. Port: inboundPort,
  44. UseTls: useTls,
  45. ForceHttpsRedirect: forceHttpsRedirect,
  46. TlsManager: tlsCertManager,
  47. RedirectRuleTable: redirectTable,
  48. GeodbStore: geodbStore,
  49. StatisticCollector: statisticCollector,
  50. })
  51. if err != nil {
  52. log.Println(err.Error())
  53. return
  54. }
  55. dynamicProxyRouter = dprouter
  56. //Load all conf from files
  57. confs, _ := filepath.Glob("./conf/*.config")
  58. for _, conf := range confs {
  59. record, err := LoadReverseProxyConfig(conf)
  60. if err != nil {
  61. log.Println("Failed to load "+filepath.Base(conf), err.Error())
  62. return
  63. }
  64. if record.ProxyType == "root" {
  65. dynamicProxyRouter.SetRootProxy(&dynamicproxy.RootOptions{
  66. ProxyLocation: record.ProxyTarget,
  67. RequireTLS: record.UseTLS,
  68. })
  69. } else if record.ProxyType == "subd" {
  70. dynamicProxyRouter.AddSubdomainRoutingService(&dynamicproxy.SubdOptions{
  71. MatchingDomain: record.Rootname,
  72. Domain: record.ProxyTarget,
  73. RequireTLS: record.UseTLS,
  74. SkipCertValidations: record.SkipTlsValidation,
  75. RequireBasicAuth: record.RequireBasicAuth,
  76. BasicAuthCredentials: record.BasicAuthCredentials,
  77. })
  78. } else if record.ProxyType == "vdir" {
  79. dynamicProxyRouter.AddVirtualDirectoryProxyService(&dynamicproxy.VdirOptions{
  80. RootName: record.Rootname,
  81. Domain: record.ProxyTarget,
  82. RequireTLS: record.UseTLS,
  83. SkipCertValidations: record.SkipTlsValidation,
  84. RequireBasicAuth: record.RequireBasicAuth,
  85. BasicAuthCredentials: record.BasicAuthCredentials,
  86. })
  87. } else {
  88. log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf))
  89. }
  90. }
  91. //Start Service
  92. //Not sure why but delay must be added if you have another
  93. //reverse proxy server in front of this service
  94. time.Sleep(300 * time.Millisecond)
  95. dynamicProxyRouter.StartProxyService()
  96. log.Println("Dynamic Reverse Proxy service started")
  97. //Add all proxy services to uptime monitor
  98. //Create a uptime monitor service
  99. go func() {
  100. //This must be done in go routine to prevent blocking on system startup
  101. uptimeMonitor, _ = uptime.NewUptimeMonitor(&uptime.Config{
  102. Targets: GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter),
  103. Interval: 300, //5 minutes
  104. MaxRecordsStore: 288, //1 day
  105. })
  106. log.Println("Uptime Monitor background service started")
  107. }()
  108. }
  109. func ReverseProxyHandleOnOff(w http.ResponseWriter, r *http.Request) {
  110. enable, _ := utils.PostPara(r, "enable") //Support root, vdir and subd
  111. if enable == "true" {
  112. err := dynamicProxyRouter.StartProxyService()
  113. if err != nil {
  114. utils.SendErrorResponse(w, err.Error())
  115. return
  116. }
  117. } else {
  118. //Check if it is loopback
  119. if dynamicProxyRouter.IsProxiedSubdomain(r) {
  120. //Loopback routing. Turning it off will make the user lost control
  121. //of the whole system. Do not allow shutdown
  122. utils.SendErrorResponse(w, "Unable to shutdown in loopback rp mode. Remove proxy rules for management interface and retry.")
  123. return
  124. }
  125. err := dynamicProxyRouter.StopProxyService()
  126. if err != nil {
  127. utils.SendErrorResponse(w, err.Error())
  128. return
  129. }
  130. }
  131. utils.SendOK(w)
  132. }
  133. func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
  134. eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
  135. if err != nil {
  136. utils.SendErrorResponse(w, "type not defined")
  137. return
  138. }
  139. endpoint, err := utils.PostPara(r, "ep")
  140. if err != nil {
  141. utils.SendErrorResponse(w, "endpoint not defined")
  142. return
  143. }
  144. tls, _ := utils.PostPara(r, "tls")
  145. if tls == "" {
  146. tls = "false"
  147. }
  148. useTLS := (tls == "true")
  149. stv, _ := utils.PostPara(r, "tlsval")
  150. if stv == "" {
  151. stv = "false"
  152. }
  153. skipTlsValidation := (stv == "true")
  154. rba, _ := utils.PostPara(r, "bauth")
  155. if rba == "" {
  156. rba = "false"
  157. }
  158. requireBasicAuth := (rba == "true")
  159. //Prase the basic auth to correct structure
  160. cred, _ := utils.PostPara(r, "cred")
  161. basicAuthCredentials := []*dynamicproxy.BasicAuthCredentials{}
  162. if requireBasicAuth {
  163. preProcessCredentials := []*dynamicproxy.BasicAuthUnhashedCredentials{}
  164. err = json.Unmarshal([]byte(cred), &preProcessCredentials)
  165. if err != nil {
  166. utils.SendErrorResponse(w, "invalid user credentials")
  167. return
  168. }
  169. //Check if there are empty password credentials
  170. for _, credObj := range preProcessCredentials {
  171. if strings.TrimSpace(credObj.Password) == "" {
  172. utils.SendErrorResponse(w, credObj.Username+" has empty password")
  173. return
  174. }
  175. }
  176. //Convert and hash the passwords
  177. for _, credObj := range preProcessCredentials {
  178. basicAuthCredentials = append(basicAuthCredentials, &dynamicproxy.BasicAuthCredentials{
  179. Username: credObj.Username,
  180. PasswordHash: auth.Hash(credObj.Password),
  181. })
  182. }
  183. }
  184. rootname := ""
  185. if eptype == "vdir" {
  186. vdir, err := utils.PostPara(r, "rootname")
  187. if err != nil {
  188. utils.SendErrorResponse(w, "vdir not defined")
  189. return
  190. }
  191. //Vdir must start with /
  192. if !strings.HasPrefix(vdir, "/") {
  193. vdir = "/" + vdir
  194. }
  195. rootname = vdir
  196. thisOption := dynamicproxy.VdirOptions{
  197. RootName: vdir,
  198. Domain: endpoint,
  199. RequireTLS: useTLS,
  200. SkipCertValidations: skipTlsValidation,
  201. RequireBasicAuth: requireBasicAuth,
  202. BasicAuthCredentials: basicAuthCredentials,
  203. }
  204. dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
  205. } else if eptype == "subd" {
  206. subdomain, err := utils.PostPara(r, "rootname")
  207. if err != nil {
  208. utils.SendErrorResponse(w, "subdomain not defined")
  209. return
  210. }
  211. rootname = subdomain
  212. thisOption := dynamicproxy.SubdOptions{
  213. MatchingDomain: subdomain,
  214. Domain: endpoint,
  215. RequireTLS: useTLS,
  216. SkipCertValidations: skipTlsValidation,
  217. RequireBasicAuth: requireBasicAuth,
  218. BasicAuthCredentials: basicAuthCredentials,
  219. }
  220. dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
  221. } else if eptype == "root" {
  222. rootname = "root"
  223. thisOption := dynamicproxy.RootOptions{
  224. ProxyLocation: endpoint,
  225. RequireTLS: useTLS,
  226. }
  227. dynamicProxyRouter.SetRootProxy(&thisOption)
  228. } else {
  229. //Invalid eptype
  230. utils.SendErrorResponse(w, "Invalid endpoint type")
  231. return
  232. }
  233. //Save it
  234. thisProxyConfigRecord := Record{
  235. ProxyType: eptype,
  236. Rootname: rootname,
  237. ProxyTarget: endpoint,
  238. UseTLS: useTLS,
  239. SkipTlsValidation: skipTlsValidation,
  240. RequireBasicAuth: requireBasicAuth,
  241. BasicAuthCredentials: basicAuthCredentials,
  242. }
  243. SaveReverseProxyConfig(&thisProxyConfigRecord)
  244. //Update utm if exists
  245. if uptimeMonitor != nil {
  246. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  247. uptimeMonitor.CleanRecords()
  248. }
  249. utils.SendOK(w)
  250. }
  251. /*
  252. ReverseProxyHandleEditEndpoint handles proxy endpoint edit
  253. This endpoint do not handle
  254. basic auth credential update. The credential
  255. will be loaded from old config and reused
  256. */
  257. func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
  258. eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
  259. if err != nil {
  260. utils.SendErrorResponse(w, "type not defined")
  261. return
  262. }
  263. rootNameOrMatchingDomain, err := utils.PostPara(r, "rootname")
  264. if err != nil {
  265. utils.SendErrorResponse(w, "Target proxy rule not defined")
  266. return
  267. }
  268. endpoint, err := utils.PostPara(r, "ep")
  269. if err != nil {
  270. utils.SendErrorResponse(w, "endpoint not defined")
  271. return
  272. }
  273. tls, _ := utils.PostPara(r, "tls")
  274. if tls == "" {
  275. tls = "false"
  276. }
  277. useTLS := (tls == "true")
  278. stv, _ := utils.PostPara(r, "tlsval")
  279. if stv == "" {
  280. stv = "false"
  281. }
  282. skipTlsValidation := (stv == "true")
  283. rba, _ := utils.PostPara(r, "bauth")
  284. if rba == "" {
  285. rba = "false"
  286. }
  287. requireBasicAuth := (rba == "true")
  288. //Load the previous basic auth credentials from current proxy rules
  289. targetProxyEntry, err := dynamicProxyRouter.LoadProxy(eptype, rootNameOrMatchingDomain)
  290. if err != nil {
  291. utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
  292. return
  293. }
  294. if eptype == "vdir" {
  295. thisOption := dynamicproxy.VdirOptions{
  296. RootName: targetProxyEntry.RootOrMatchingDomain,
  297. Domain: endpoint,
  298. RequireTLS: useTLS,
  299. SkipCertValidations: skipTlsValidation,
  300. RequireBasicAuth: requireBasicAuth,
  301. BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
  302. }
  303. dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
  304. } else if eptype == "subd" {
  305. thisOption := dynamicproxy.SubdOptions{
  306. MatchingDomain: targetProxyEntry.RootOrMatchingDomain,
  307. Domain: endpoint,
  308. RequireTLS: useTLS,
  309. SkipCertValidations: skipTlsValidation,
  310. RequireBasicAuth: requireBasicAuth,
  311. BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
  312. }
  313. dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
  314. }
  315. //Save it to file
  316. thisProxyConfigRecord := Record{
  317. ProxyType: eptype,
  318. Rootname: targetProxyEntry.RootOrMatchingDomain,
  319. ProxyTarget: endpoint,
  320. UseTLS: useTLS,
  321. SkipTlsValidation: skipTlsValidation,
  322. RequireBasicAuth: requireBasicAuth,
  323. BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
  324. }
  325. SaveReverseProxyConfig(&thisProxyConfigRecord)
  326. //Update the current running config
  327. targetProxyEntry.Domain = endpoint
  328. targetProxyEntry.RequireTLS = useTLS
  329. targetProxyEntry.SkipCertValidations = skipTlsValidation
  330. targetProxyEntry.RequireBasicAuth = requireBasicAuth
  331. dynamicProxyRouter.SaveProxy(eptype, targetProxyEntry.RootOrMatchingDomain, targetProxyEntry)
  332. utils.SendOK(w)
  333. }
  334. func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
  335. ep, err := utils.GetPara(r, "ep")
  336. if err != nil {
  337. utils.SendErrorResponse(w, "Invalid ep given")
  338. }
  339. ptype, err := utils.PostPara(r, "ptype")
  340. if err != nil {
  341. utils.SendErrorResponse(w, "Invalid ptype given")
  342. }
  343. err = dynamicProxyRouter.RemoveProxy(ptype, ep)
  344. if err != nil {
  345. utils.SendErrorResponse(w, err.Error())
  346. }
  347. RemoveReverseProxyConfig(ep)
  348. //Update utm if exists
  349. if uptimeMonitor != nil {
  350. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  351. uptimeMonitor.CleanRecords()
  352. }
  353. utils.SendOK(w)
  354. }
  355. func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
  356. js, _ := json.Marshal(dynamicProxyRouter)
  357. utils.SendJSONResponse(w, string(js))
  358. }
  359. func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
  360. eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
  361. if err != nil {
  362. utils.SendErrorResponse(w, "type not defined")
  363. return
  364. }
  365. if eptype == "vdir" {
  366. results := []*dynamicproxy.ProxyEndpoint{}
  367. dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
  368. results = append(results, value.(*dynamicproxy.ProxyEndpoint))
  369. return true
  370. })
  371. sort.Slice(results, func(i, j int) bool {
  372. return results[i].Domain < results[j].Domain
  373. })
  374. js, _ := json.Marshal(results)
  375. utils.SendJSONResponse(w, string(js))
  376. } else if eptype == "subd" {
  377. results := []*dynamicproxy.ProxyEndpoint{}
  378. dynamicProxyRouter.SubdomainEndpoint.Range(func(key, value interface{}) bool {
  379. results = append(results, value.(*dynamicproxy.ProxyEndpoint))
  380. return true
  381. })
  382. sort.Slice(results, func(i, j int) bool {
  383. return results[i].RootOrMatchingDomain < results[j].RootOrMatchingDomain
  384. })
  385. js, _ := json.Marshal(results)
  386. utils.SendJSONResponse(w, string(js))
  387. } else if eptype == "root" {
  388. js, _ := json.Marshal(dynamicProxyRouter.Root)
  389. utils.SendJSONResponse(w, string(js))
  390. } else {
  391. utils.SendErrorResponse(w, "Invalid type given")
  392. }
  393. }
  394. // Handle https redirect
  395. func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
  396. useRedirect, err := utils.GetPara(r, "set")
  397. if err != nil {
  398. currentRedirectToHttps := false
  399. //Load the current status
  400. err = sysdb.Read("settings", "redirect", &currentRedirectToHttps)
  401. if err != nil {
  402. utils.SendErrorResponse(w, err.Error())
  403. return
  404. }
  405. js, _ := json.Marshal(currentRedirectToHttps)
  406. utils.SendJSONResponse(w, string(js))
  407. } else {
  408. if useRedirect == "true" {
  409. sysdb.Write("settings", "redirect", true)
  410. log.Println("Updating force HTTPS redirection to true")
  411. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
  412. } else if useRedirect == "false" {
  413. sysdb.Write("settings", "redirect", false)
  414. log.Println("Updating force HTTPS redirection to false")
  415. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
  416. }
  417. utils.SendOK(w)
  418. }
  419. }
  420. // Handle checking if the current user is accessing via the reverse proxied interface
  421. // Of the management interface.
  422. func HandleManagementProxyCheck(w http.ResponseWriter, r *http.Request) {
  423. isProxied := dynamicProxyRouter.IsProxiedSubdomain(r)
  424. js, _ := json.Marshal(isProxied)
  425. utils.SendJSONResponse(w, string(js))
  426. }
  427. // Handle incoming port set. Change the current proxy incoming port
  428. func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
  429. newIncomingPort, err := utils.PostPara(r, "incoming")
  430. if err != nil {
  431. utils.SendErrorResponse(w, "invalid incoming port given")
  432. return
  433. }
  434. newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
  435. if err != nil {
  436. utils.SendErrorResponse(w, "invalid incoming port given")
  437. return
  438. }
  439. //Check if it is identical as proxy root (recursion!)
  440. proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/")
  441. if strings.HasPrefix(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.HasPrefix(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) {
  442. //Listening port is same as proxy root
  443. //Not allow recursive settings
  444. utils.SendErrorResponse(w, "Recursive listening port! Check your proxy root settings.")
  445. return
  446. }
  447. //Stop and change the setting of the reverse proxy service
  448. if dynamicProxyRouter.Running {
  449. dynamicProxyRouter.StopProxyService()
  450. dynamicProxyRouter.Option.Port = newIncomingPortInt
  451. dynamicProxyRouter.StartProxyService()
  452. } else {
  453. //Only change setting but not starting the proxy service
  454. dynamicProxyRouter.Option.Port = newIncomingPortInt
  455. }
  456. sysdb.Write("settings", "inbound", newIncomingPortInt)
  457. utils.SendOK(w)
  458. }