reverseproxy.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  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/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. log.Println("Serving inbound port ", inboundPort)
  24. } else {
  25. log.Println("Inbound port not set. Using default (80)")
  26. }
  27. useTls := false
  28. sysdb.Read("settings", "usetls", &useTls)
  29. if useTls {
  30. log.Println("TLS mode enabled. Serving proxxy request with TLS")
  31. } else {
  32. log.Println("TLS mode disabled. Serving proxy request with plain http")
  33. }
  34. forceHttpsRedirect := false
  35. sysdb.Read("settings", "redirect", &forceHttpsRedirect)
  36. if forceHttpsRedirect {
  37. log.Println("Force HTTPS mode enabled")
  38. } else {
  39. log.Println("Force HTTPS mode disabled")
  40. }
  41. dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
  42. Port: inboundPort,
  43. UseTls: useTls,
  44. ForceHttpsRedirect: forceHttpsRedirect,
  45. TlsManager: tlsCertManager,
  46. RedirectRuleTable: redirectTable,
  47. GeodbStore: geodbStore,
  48. StatisticCollector: statisticCollector,
  49. })
  50. if err != nil {
  51. log.Println(err.Error())
  52. return
  53. }
  54. dynamicProxyRouter = dprouter
  55. //Load all conf from files
  56. confs, _ := filepath.Glob("./conf/*.config")
  57. for _, conf := range confs {
  58. record, err := LoadReverseProxyConfig(conf)
  59. if err != nil {
  60. log.Println("Failed to load "+filepath.Base(conf), err.Error())
  61. return
  62. }
  63. if record.ProxyType == "root" {
  64. dynamicProxyRouter.SetRootProxy(&dynamicproxy.RootOptions{
  65. ProxyLocation: record.ProxyTarget,
  66. RequireTLS: record.UseTLS,
  67. })
  68. } else if record.ProxyType == "subd" {
  69. dynamicProxyRouter.AddSubdomainRoutingService(&dynamicproxy.SubdOptions{
  70. MatchingDomain: record.Rootname,
  71. Domain: record.ProxyTarget,
  72. RequireTLS: record.UseTLS,
  73. })
  74. } else if record.ProxyType == "vdir" {
  75. dynamicProxyRouter.AddVirtualDirectoryProxyService(&dynamicproxy.VdirOptions{
  76. RootName: record.Rootname,
  77. Domain: record.ProxyTarget,
  78. RequireTLS: record.UseTLS,
  79. })
  80. } else {
  81. log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf))
  82. }
  83. }
  84. /*
  85. dynamicProxyRouter.SetRootProxy("192.168.0.107:8080", false)
  86. dynamicProxyRouter.AddSubdomainRoutingService("aroz.localhost", "192.168.0.107:8080/private/AOB/", false)
  87. dynamicProxyRouter.AddSubdomainRoutingService("loopback.localhost", "localhost:8080", false)
  88. dynamicProxyRouter.AddSubdomainRoutingService("git.localhost", "mc.alanyeung.co:3000", false)
  89. dynamicProxyRouter.AddVirtualDirectoryProxyService("/git/server/", "mc.alanyeung.co:3000", false)
  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. rootname := ""
  150. if eptype == "vdir" {
  151. vdir, err := utils.PostPara(r, "rootname")
  152. if err != nil {
  153. utils.SendErrorResponse(w, "vdir not defined")
  154. return
  155. }
  156. //Vdir must start with /
  157. if !strings.HasPrefix(vdir, "/") {
  158. vdir = "/" + vdir
  159. }
  160. rootname = vdir
  161. thisOption := dynamicproxy.VdirOptions{
  162. RootName: vdir,
  163. Domain: endpoint,
  164. RequireTLS: useTLS,
  165. }
  166. dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
  167. } else if eptype == "subd" {
  168. subdomain, err := utils.PostPara(r, "rootname")
  169. if err != nil {
  170. utils.SendErrorResponse(w, "subdomain not defined")
  171. return
  172. }
  173. rootname = subdomain
  174. thisOption := dynamicproxy.SubdOptions{
  175. MatchingDomain: subdomain,
  176. Domain: endpoint,
  177. RequireTLS: useTLS,
  178. }
  179. dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
  180. } else if eptype == "root" {
  181. rootname = "root"
  182. thisOption := dynamicproxy.RootOptions{
  183. ProxyLocation: endpoint,
  184. RequireTLS: useTLS,
  185. }
  186. dynamicProxyRouter.SetRootProxy(&thisOption)
  187. } else {
  188. //Invalid eptype
  189. utils.SendErrorResponse(w, "Invalid endpoint type")
  190. return
  191. }
  192. //Save it
  193. SaveReverseProxyConfig(eptype, rootname, endpoint, useTLS)
  194. //Update utm if exists
  195. if uptimeMonitor != nil {
  196. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  197. uptimeMonitor.CleanRecords()
  198. }
  199. utils.SendOK(w)
  200. }
  201. func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
  202. ep, err := utils.GetPara(r, "ep")
  203. if err != nil {
  204. utils.SendErrorResponse(w, "Invalid ep given")
  205. }
  206. ptype, err := utils.PostPara(r, "ptype")
  207. if err != nil {
  208. utils.SendErrorResponse(w, "Invalid ptype given")
  209. }
  210. err = dynamicProxyRouter.RemoveProxy(ptype, ep)
  211. if err != nil {
  212. utils.SendErrorResponse(w, err.Error())
  213. }
  214. RemoveReverseProxyConfig(ep)
  215. //Update utm if exists
  216. if uptimeMonitor != nil {
  217. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  218. uptimeMonitor.CleanRecords()
  219. }
  220. utils.SendOK(w)
  221. }
  222. func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
  223. js, _ := json.Marshal(dynamicProxyRouter)
  224. utils.SendJSONResponse(w, string(js))
  225. }
  226. func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
  227. eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
  228. if err != nil {
  229. utils.SendErrorResponse(w, "type not defined")
  230. return
  231. }
  232. if eptype == "vdir" {
  233. results := []*dynamicproxy.ProxyEndpoint{}
  234. dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
  235. results = append(results, value.(*dynamicproxy.ProxyEndpoint))
  236. return true
  237. })
  238. sort.Slice(results, func(i, j int) bool {
  239. return results[i].Domain < results[j].Domain
  240. })
  241. js, _ := json.Marshal(results)
  242. utils.SendJSONResponse(w, string(js))
  243. } else if eptype == "subd" {
  244. results := []*dynamicproxy.ProxyEndpoint{}
  245. dynamicProxyRouter.SubdomainEndpoint.Range(func(key, value interface{}) bool {
  246. results = append(results, value.(*dynamicproxy.ProxyEndpoint))
  247. return true
  248. })
  249. sort.Slice(results, func(i, j int) bool {
  250. return results[i].RootOrMatchingDomain < results[j].RootOrMatchingDomain
  251. })
  252. js, _ := json.Marshal(results)
  253. utils.SendJSONResponse(w, string(js))
  254. } else if eptype == "root" {
  255. js, _ := json.Marshal(dynamicProxyRouter.Root)
  256. utils.SendJSONResponse(w, string(js))
  257. } else {
  258. utils.SendErrorResponse(w, "Invalid type given")
  259. }
  260. }
  261. // Handle https redirect
  262. func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
  263. useRedirect, err := utils.GetPara(r, "set")
  264. if err != nil {
  265. currentRedirectToHttps := false
  266. //Load the current status
  267. err = sysdb.Read("settings", "redirect", &currentRedirectToHttps)
  268. if err != nil {
  269. utils.SendErrorResponse(w, err.Error())
  270. return
  271. }
  272. js, _ := json.Marshal(currentRedirectToHttps)
  273. utils.SendJSONResponse(w, string(js))
  274. } else {
  275. if useRedirect == "true" {
  276. sysdb.Write("settings", "redirect", true)
  277. log.Println("Updating force HTTPS redirection to true")
  278. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
  279. } else if useRedirect == "false" {
  280. sysdb.Write("settings", "redirect", false)
  281. log.Println("Updating force HTTPS redirection to false")
  282. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
  283. }
  284. utils.SendOK(w)
  285. }
  286. }
  287. // Handle checking if the current user is accessing via the reverse proxied interface
  288. // Of the management interface.
  289. func HandleManagementProxyCheck(w http.ResponseWriter, r *http.Request) {
  290. isProxied := dynamicProxyRouter.IsProxiedSubdomain(r)
  291. js, _ := json.Marshal(isProxied)
  292. utils.SendJSONResponse(w, string(js))
  293. }
  294. // Handle incoming port set. Change the current proxy incoming port
  295. func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
  296. newIncomingPort, err := utils.PostPara(r, "incoming")
  297. if err != nil {
  298. utils.SendErrorResponse(w, "invalid incoming port given")
  299. return
  300. }
  301. newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
  302. if err != nil {
  303. utils.SendErrorResponse(w, "invalid incoming port given")
  304. return
  305. }
  306. //Check if it is identical as proxy root (recursion!)
  307. proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/")
  308. if strings.HasPrefix(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.HasPrefix(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) {
  309. //Listening port is same as proxy root
  310. //Not allow recursive settings
  311. utils.SendErrorResponse(w, "Recursive listening port! Check your proxy root settings.")
  312. return
  313. }
  314. //Stop and change the setting of the reverse proxy service
  315. if dynamicProxyRouter.Running {
  316. dynamicProxyRouter.StopProxyService()
  317. dynamicProxyRouter.Option.Port = newIncomingPortInt
  318. dynamicProxyRouter.StartProxyService()
  319. } else {
  320. //Only change setting but not starting the proxy service
  321. dynamicProxyRouter.Option.Port = newIncomingPortInt
  322. }
  323. sysdb.Write("settings", "inbound", newIncomingPortInt)
  324. utils.SendOK(w)
  325. }