1
0

reverseproxy.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  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. func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
  252. ep, err := utils.GetPara(r, "ep")
  253. if err != nil {
  254. utils.SendErrorResponse(w, "Invalid ep given")
  255. }
  256. ptype, err := utils.PostPara(r, "ptype")
  257. if err != nil {
  258. utils.SendErrorResponse(w, "Invalid ptype given")
  259. }
  260. err = dynamicProxyRouter.RemoveProxy(ptype, ep)
  261. if err != nil {
  262. utils.SendErrorResponse(w, err.Error())
  263. }
  264. RemoveReverseProxyConfig(ep)
  265. //Update utm if exists
  266. if uptimeMonitor != nil {
  267. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  268. uptimeMonitor.CleanRecords()
  269. }
  270. utils.SendOK(w)
  271. }
  272. func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
  273. js, _ := json.Marshal(dynamicProxyRouter)
  274. utils.SendJSONResponse(w, string(js))
  275. }
  276. func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
  277. eptype, err := utils.PostPara(r, "type") //Support root, vdir and subd
  278. if err != nil {
  279. utils.SendErrorResponse(w, "type not defined")
  280. return
  281. }
  282. if eptype == "vdir" {
  283. results := []*dynamicproxy.ProxyEndpoint{}
  284. dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
  285. results = append(results, value.(*dynamicproxy.ProxyEndpoint))
  286. return true
  287. })
  288. sort.Slice(results, func(i, j int) bool {
  289. return results[i].Domain < results[j].Domain
  290. })
  291. js, _ := json.Marshal(results)
  292. utils.SendJSONResponse(w, string(js))
  293. } else if eptype == "subd" {
  294. results := []*dynamicproxy.ProxyEndpoint{}
  295. dynamicProxyRouter.SubdomainEndpoint.Range(func(key, value interface{}) bool {
  296. results = append(results, value.(*dynamicproxy.ProxyEndpoint))
  297. return true
  298. })
  299. sort.Slice(results, func(i, j int) bool {
  300. return results[i].RootOrMatchingDomain < results[j].RootOrMatchingDomain
  301. })
  302. js, _ := json.Marshal(results)
  303. utils.SendJSONResponse(w, string(js))
  304. } else if eptype == "root" {
  305. js, _ := json.Marshal(dynamicProxyRouter.Root)
  306. utils.SendJSONResponse(w, string(js))
  307. } else {
  308. utils.SendErrorResponse(w, "Invalid type given")
  309. }
  310. }
  311. // Handle https redirect
  312. func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
  313. useRedirect, err := utils.GetPara(r, "set")
  314. if err != nil {
  315. currentRedirectToHttps := false
  316. //Load the current status
  317. err = sysdb.Read("settings", "redirect", &currentRedirectToHttps)
  318. if err != nil {
  319. utils.SendErrorResponse(w, err.Error())
  320. return
  321. }
  322. js, _ := json.Marshal(currentRedirectToHttps)
  323. utils.SendJSONResponse(w, string(js))
  324. } else {
  325. if useRedirect == "true" {
  326. sysdb.Write("settings", "redirect", true)
  327. log.Println("Updating force HTTPS redirection to true")
  328. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
  329. } else if useRedirect == "false" {
  330. sysdb.Write("settings", "redirect", false)
  331. log.Println("Updating force HTTPS redirection to false")
  332. dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
  333. }
  334. utils.SendOK(w)
  335. }
  336. }
  337. // Handle checking if the current user is accessing via the reverse proxied interface
  338. // Of the management interface.
  339. func HandleManagementProxyCheck(w http.ResponseWriter, r *http.Request) {
  340. isProxied := dynamicProxyRouter.IsProxiedSubdomain(r)
  341. js, _ := json.Marshal(isProxied)
  342. utils.SendJSONResponse(w, string(js))
  343. }
  344. // Handle incoming port set. Change the current proxy incoming port
  345. func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
  346. newIncomingPort, err := utils.PostPara(r, "incoming")
  347. if err != nil {
  348. utils.SendErrorResponse(w, "invalid incoming port given")
  349. return
  350. }
  351. newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
  352. if err != nil {
  353. utils.SendErrorResponse(w, "invalid incoming port given")
  354. return
  355. }
  356. //Check if it is identical as proxy root (recursion!)
  357. proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/")
  358. if strings.HasPrefix(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.HasPrefix(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) {
  359. //Listening port is same as proxy root
  360. //Not allow recursive settings
  361. utils.SendErrorResponse(w, "Recursive listening port! Check your proxy root settings.")
  362. return
  363. }
  364. //Stop and change the setting of the reverse proxy service
  365. if dynamicProxyRouter.Running {
  366. dynamicProxyRouter.StopProxyService()
  367. dynamicProxyRouter.Option.Port = newIncomingPortInt
  368. dynamicProxyRouter.StartProxyService()
  369. } else {
  370. //Only change setting but not starting the proxy service
  371. dynamicProxyRouter.Option.Port = newIncomingPortInt
  372. }
  373. sysdb.Write("settings", "inbound", newIncomingPortInt)
  374. utils.SendOK(w)
  375. }