diff --git a/src/acme.go b/src/acme.go index a04a257..cdd04dc 100644 --- a/src/acme.go +++ b/src/acme.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "log" "math/rand" "net/http" "regexp" @@ -29,7 +28,7 @@ func getRandomPort(minPort int) int { // init the new ACME instance func initACME() *acme.ACMEHandler { - log.Println("Starting ACME handler") + SystemWideLogger.Println("Starting ACME handler") rand.Seed(time.Now().UnixNano()) // Generate a random port above 30000 port := getRandomPort(30000) @@ -44,7 +43,7 @@ func initACME() *acme.ACMEHandler { // create the special routing rule for ACME func acmeRegisterSpecialRoutingRule() { - log.Println("Assigned temporary port:" + acmeHandler.Getport()) + SystemWideLogger.Println("Assigned temporary port:" + acmeHandler.Getport()) err := dynamicProxyRouter.AddRoutingRules(&dynamicproxy.RoutingRule{ ID: "acme-autorenew", @@ -79,7 +78,7 @@ func acmeRegisterSpecialRoutingRule() { }) if err != nil { - log.Println("[Err] " + err.Error()) + SystemWideLogger.PrintAndLog("ACME", "Unable register temp port for DNS resolver", err) } } @@ -89,7 +88,7 @@ func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request) if dynamicProxyRouter.Option.Port == 443 { //Enable port 80 to 443 redirect if !dynamicProxyRouter.Option.ForceHttpsRedirect { - log.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests") + SystemWideLogger.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests") dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true) } else { //Set this to true, so after renew, do not turn it off @@ -110,7 +109,7 @@ func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request) if dynamicProxyRouter.Option.Port == 443 { if !isForceHttpsRedirectEnabledOriginally { //Default is off. Turn the redirection off - log.Println("Restoring HTTP to HTTPS redirect settings") + SystemWideLogger.PrintAndLog("ACME", "Restoring HTTP to HTTPS redirect settings", nil) dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false) } } @@ -130,7 +129,7 @@ func HandleACMEPreferredCA(w http.ResponseWriter, r *http.Request) { acme.IsSupportedCA(ca) //Set the new config sysdb.Write("acmepref", "prefca", ca) - log.Println("Updating prefered ACME CA to " + ca) + SystemWideLogger.Println("Updating prefered ACME CA to " + ca) utils.SendOK(w) } diff --git a/src/api.go b/src/api.go index 5b52342..5831564 100644 --- a/src/api.go +++ b/src/api.go @@ -54,6 +54,7 @@ func initAPIs() { authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS) authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet) authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect) + authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener) authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck) //Reverse proxy root related APIs authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList) diff --git a/src/cert.go b/src/cert.go index e0f3464..567fd7c 100644 --- a/src/cert.go +++ b/src/cert.go @@ -6,7 +6,6 @@ import ( "encoding/pem" "fmt" "io" - "log" "net/http" "os" "path/filepath" @@ -128,7 +127,7 @@ func handleListDomains(w http.ResponseWriter, r *http.Request) { certBtyes, err := os.ReadFile(certFilepath) if err != nil { // Unable to load this file - log.Println("Unable to load certificate: " + certFilepath) + SystemWideLogger.PrintAndLog("TLS", "Unable to load certificate: "+certFilepath, err) continue } else { // Cert loaded. Check its expiry time @@ -182,11 +181,11 @@ func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) { } else { if newState == "true" { sysdb.Write("settings", "usetls", true) - log.Println("Enabling TLS mode on reverse proxy") + SystemWideLogger.Println("Enabling TLS mode on reverse proxy") dynamicProxyRouter.UpdateTLSSetting(true) } else if newState == "false" { sysdb.Write("settings", "usetls", false) - log.Println("Disabling TLS mode on reverse proxy") + SystemWideLogger.Println("Disabling TLS mode on reverse proxy") dynamicProxyRouter.UpdateTLSSetting(false) } else { utils.SendErrorResponse(w, "invalid state given. Only support true or false") @@ -213,11 +212,11 @@ func handleSetTlsRequireLatest(w http.ResponseWriter, r *http.Request) { } else { if newState == "true" { sysdb.Write("settings", "forceLatestTLS", true) - log.Println("Updating minimum TLS version to v1.2 or above") + SystemWideLogger.Println("Updating minimum TLS version to v1.2 or above") dynamicProxyRouter.UpdateTLSVersion(true) } else if newState == "false" { sysdb.Write("settings", "forceLatestTLS", false) - log.Println("Updating minimum TLS version to v1.0 or above") + SystemWideLogger.Println("Updating minimum TLS version to v1.0 or above") dynamicProxyRouter.UpdateTLSVersion(false) } else { utils.SendErrorResponse(w, "invalid state given") diff --git a/src/config.go b/src/config.go index 9150476..c012467 100644 --- a/src/config.go +++ b/src/config.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "log" "net/http" "os" "path/filepath" @@ -29,6 +28,7 @@ type Record struct { Rootname string ProxyTarget string UseTLS bool + BypassGlobalTLS bool SkipTlsValidation bool RequireBasicAuth bool BasicAuthCredentials []*dynamicproxy.BasicAuthCredentials @@ -61,11 +61,11 @@ func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) e func RemoveReverseProxyConfigFile(rootname string) error { filename := getFilenameFromRootName(rootname) removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/") - log.Println("Config Removed: ", removePendingFile) + SystemWideLogger.Println("Config Removed: ", removePendingFile) if utils.FileExists(removePendingFile) { err := os.Remove(removePendingFile) if err != nil { - log.Println(err.Error()) + SystemWideLogger.PrintAndLog("Proxy", "Unabel to remove config file", err) return err } } @@ -81,6 +81,7 @@ func LoadReverseProxyConfig(filename string) (*Record, error) { Rootname: "", ProxyTarget: "", UseTLS: false, + BypassGlobalTLS: false, SkipTlsValidation: false, RequireBasicAuth: false, BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{}, @@ -109,6 +110,7 @@ func ConvertProxyEndpointToRecord(targetProxyEndpoint *dynamicproxy.ProxyEndpoin Rootname: targetProxyEndpoint.RootOrMatchingDomain, ProxyTarget: targetProxyEndpoint.Domain, UseTLS: targetProxyEndpoint.RequireTLS, + BypassGlobalTLS: targetProxyEndpoint.BypassGlobalTLS, SkipTlsValidation: targetProxyEndpoint.SkipCertValidations, RequireBasicAuth: targetProxyEndpoint.RequireBasicAuth, BasicAuthCredentials: targetProxyEndpoint.BasicAuthCredentials, @@ -191,14 +193,14 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) { //Also zip in the sysdb zipFile, err := zipWriter.Create("sys.db") if err != nil { - log.Println("[Backup] Unable to zip sysdb: " + err.Error()) + SystemWideLogger.PrintAndLog("Backup", "Unable to zip sysdb", err) return } // Open the file on disk file, err := os.Open("sys.db") if err != nil { - log.Println("[Backup] Unable to open sysdb: " + err.Error()) + SystemWideLogger.PrintAndLog("Backup", "Unable to open sysdb", err) return } defer file.Close() @@ -206,7 +208,7 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) { // Copy the file contents to the zip file _, err = io.Copy(zipFile, file) if err != nil { - log.Println(err) + SystemWideLogger.Println(err) return } @@ -311,12 +313,12 @@ func ImportConfigFromZip(w http.ResponseWriter, r *http.Request) { // Send a success response w.WriteHeader(http.StatusOK) - log.Println("Configuration restored") + SystemWideLogger.Println("Configuration restored") fmt.Fprintln(w, "Configuration restored") if restoreDatabase { go func() { - log.Println("Database altered. Restarting in 3 seconds...") + SystemWideLogger.Println("Database altered. Restarting in 3 seconds...") time.Sleep(3 * time.Second) os.Exit(0) }() diff --git a/src/main.go b/src/main.go index 4177a75..ae49a91 100644 --- a/src/main.go +++ b/src/main.go @@ -20,6 +20,7 @@ import ( "imuslab.com/zoraxy/mod/email" "imuslab.com/zoraxy/mod/ganserv" "imuslab.com/zoraxy/mod/geodb" + "imuslab.com/zoraxy/mod/info/logger" "imuslab.com/zoraxy/mod/mdns" "imuslab.com/zoraxy/mod/netstat" "imuslab.com/zoraxy/mod/pathrule" @@ -44,10 +45,11 @@ var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL cert var enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)") var staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters") var allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder") +var logOutputToFile = flag.Bool("log", true, "Log terminal output to file") var ( name = "Zoraxy" - version = "2.6.7" + version = "2.6.8" nodeUUID = "generic" development = false //Set this to false to use embedded web fs bootTime = time.Now().Unix() @@ -80,8 +82,9 @@ var ( staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs //Helper modules - EmailSender *email.Sender //Email sender that handle email sending - AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic + EmailSender *email.Sender //Email sender that handle email sending + AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic + SystemWideLogger *logger.Logger //Logger for Zoraxy ) // Kill signal handler. Do something before the system the core terminate. @@ -116,6 +119,9 @@ func ShutdownSeq() { fmt.Println("- Cleaning up tmp files") os.RemoveAll("./tmp") + fmt.Println("- Closing system wide logger") + SystemWideLogger.Close() + //Close database, final fmt.Println("- Stopping system database") sysdb.Close() @@ -151,7 +157,7 @@ func main() { } uuidBytes, err := os.ReadFile(uuidRecord) if err != nil { - log.Println("Unable to read system uuid from file system") + SystemWideLogger.PrintAndLog("ZeroTier", "Unable to read system uuid from file system", nil) panic(err) } nodeUUID = string(uuidBytes) @@ -173,7 +179,7 @@ func main() { //Start the finalize sequences finalSequence() - log.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port) + SystemWideLogger.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port) err = http.ListenAndServe(handler.Port, nil) if err != nil { diff --git a/src/mod/dynamicproxy/dynamicproxy.go b/src/mod/dynamicproxy/dynamicproxy.go index 8b5d750..d5f5d07 100644 --- a/src/mod/dynamicproxy/dynamicproxy.go +++ b/src/mod/dynamicproxy/dynamicproxy.go @@ -60,6 +60,12 @@ func (router *Router) UpdateTLSVersion(requireLatest bool) { router.Restart() } +// Update port 80 listener state +func (router *Router) UpdatePort80ListenerState(useRedirect bool) { + router.Option.ListenOnPort80 = useRedirect + router.Restart() +} + // Update https redirect, which will require updates func (router *Router) UpdateHttpToHttpsRedirectSetting(useRedirect bool) { router.Option.ForceHttpsRedirect = useRedirect @@ -95,27 +101,73 @@ func (router *Router) StartProxyService() error { } if router.Option.UseTls { - //Serve with TLS mode - ln, err := tls.Listen("tcp", ":"+strconv.Itoa(router.Option.Port), config) - if err != nil { - log.Println(err) - router.Running = false - return err + /* + //Serve with TLS mode + ln, err := tls.Listen("tcp", ":"+strconv.Itoa(router.Option.Port), config) + if err != nil { + log.Println(err) + router.Running = false + return err + } + router.tlsListener = ln + */ + router.server = &http.Server{ + Addr: ":" + strconv.Itoa(router.Option.Port), + Handler: router.mux, + TLSConfig: config, } - router.tlsListener = ln - router.server = &http.Server{Addr: ":" + strconv.Itoa(router.Option.Port), Handler: router.mux} router.Running = true - if router.Option.Port != 80 && router.Option.ForceHttpsRedirect { + if router.Option.Port != 80 && router.Option.ListenOnPort80 { //Add a 80 to 443 redirector httpServer := &http.Server{ Addr: ":80", Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - protocol := "https://" - if router.Option.Port == 443 { - http.Redirect(w, r, protocol+r.Host+r.RequestURI, http.StatusTemporaryRedirect) + //Check if the domain requesting allow non TLS mode + domainOnly := r.Host + if strings.Contains(r.Host, ":") { + hostPath := strings.Split(r.Host, ":") + domainOnly = hostPath[0] + } + sep := router.getSubdomainProxyEndpointFromHostname(domainOnly) + if sep != nil && sep.BypassGlobalTLS { + //Allow routing via non-TLS handler + originalHostHeader := r.Host + if r.URL != nil { + r.Host = r.URL.Host + } else { + //Fallback when the upstream proxy screw something up in the header + r.URL, _ = url.Parse(originalHostHeader) + } + + sep.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{ + ProxyDomain: sep.Domain, + OriginalHost: originalHostHeader, + UseTLS: sep.RequireTLS, + PathPrefix: "", + }) + return + } + + if router.Option.ForceHttpsRedirect { + //Redirect to https is enabled + protocol := "https://" + if router.Option.Port == 443 { + http.Redirect(w, r, protocol+r.Host+r.RequestURI, http.StatusTemporaryRedirect) + } else { + http.Redirect(w, r, protocol+r.Host+":"+strconv.Itoa(router.Option.Port)+r.RequestURI, http.StatusTemporaryRedirect) + } } else { - http.Redirect(w, r, protocol+r.Host+":"+strconv.Itoa(router.Option.Port)+r.RequestURI, http.StatusTemporaryRedirect) + //Do not do redirection + if sep != nil { + //Sub-domain exists but not allow non-TLS access + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("400 - Bad Request")) + } else { + //No defined sub-domain + http.NotFound(w, r) + } + } }), @@ -143,7 +195,7 @@ func (router *Router) StartProxyService() error { if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { //Unable to startup port 80 listener. Handle shutdown process gracefully stopChan <- true - log.Fatalf("Could not start server: %v\n", err) + log.Fatalf("Could not start redirection server: %v\n", err) } }() router.tlsRedirectStop = stopChan @@ -152,8 +204,8 @@ func (router *Router) StartProxyService() error { //Start the TLS server log.Println("Reverse proxy service started in the background (TLS mode)") go func() { - if err := router.server.Serve(ln); err != nil && err != http.ErrServerClosed { - log.Fatalf("Could not start server: %v\n", err) + if err := router.server.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed { + log.Fatalf("Could not start proxy server: %v\n", err) } }() } else { diff --git a/src/mod/dynamicproxy/subdomain.go b/src/mod/dynamicproxy/subdomain.go index 9012724..f01e541 100644 --- a/src/mod/dynamicproxy/subdomain.go +++ b/src/mod/dynamicproxy/subdomain.go @@ -38,6 +38,7 @@ func (router *Router) AddSubdomainRoutingService(options *SubdOptions) error { Domain: domain, RequireTLS: options.RequireTLS, Proxy: proxy, + BypassGlobalTLS: options.BypassGlobalTLS, SkipCertValidations: options.SkipCertValidations, RequireBasicAuth: options.RequireBasicAuth, BasicAuthCredentials: options.BasicAuthCredentials, diff --git a/src/mod/dynamicproxy/typedef.go b/src/mod/dynamicproxy/typedef.go index 2372f04..43c5fa2 100644 --- a/src/mod/dynamicproxy/typedef.go +++ b/src/mod/dynamicproxy/typedef.go @@ -27,6 +27,7 @@ type RouterOption struct { Port int //Incoming port UseTls bool //Use TLS to serve incoming requsts ForceTLSLatest bool //Force TLS1.2 or above + ListenOnPort80 bool //Enable port 80 http listener ForceHttpsRedirect bool //Force redirection of http to https endpoint TlsManager *tlscert.Manager RedirectRuleTable *redirection.RuleTable diff --git a/src/mod/info/logger/logger.go b/src/mod/info/logger/logger.go new file mode 100644 index 0000000..bc69ab4 --- /dev/null +++ b/src/mod/info/logger/logger.go @@ -0,0 +1,103 @@ +package logger + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strconv" + "time" +) + +/* + Zoraxy Logger + + This script is designed to make a managed log for the Zoraxy + and replace the ton of log.Println in the system core +*/ + +type Logger struct { + LogToFile bool //Set enable write to file + Prefix string //Prefix for log files + LogFolder string //Folder to store the log file + CurrentLogFile string //Current writing filename + file *os.File +} + +func NewLogger(logFilePrefix string, logFolder string, logToFile bool) (*Logger, error) { + err := os.MkdirAll(logFolder, 0775) + if err != nil { + return nil, err + } + + thisLogger := Logger{ + LogToFile: logToFile, + Prefix: logFilePrefix, + LogFolder: logFolder, + } + + logFilePath := thisLogger.getLogFilepath() + f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) + if err != nil { + return nil, err + } + thisLogger.CurrentLogFile = logFilePath + thisLogger.file = f + return &thisLogger, nil +} + +func (l *Logger) getLogFilepath() string { + year, month, _ := time.Now().Date() + return filepath.Join(l.LogFolder, l.Prefix+"_"+strconv.Itoa(year)+"-"+strconv.Itoa(int(month))+".log") +} + +// PrintAndLog will log the message to file and print the log to STDOUT +func (l *Logger) PrintAndLog(title string, message string, originalError error) { + go func() { + l.Log(title, message, originalError) + }() + log.Println("[" + title + "] " + message) +} + +// Println is a fast snap-in replacement for log.Println +func (l *Logger) Println(v ...interface{}) { + //Convert the array of interfaces into string + message := fmt.Sprint(v...) + go func() { + l.Log("info", string(message), nil) + }() + log.Println("[INFO] " + string(message)) +} + +func (l *Logger) Log(title string, errorMessage string, originalError error) { + l.ValidateAndUpdateLogFilepath() + if l.LogToFile { + if originalError == nil { + l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [INFO]" + errorMessage + "\n") + } else { + l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [ERROR]" + errorMessage + " " + originalError.Error() + "\n") + } + } + +} + +// Validate if the logging target is still valid (detect any months change) +func (l *Logger) ValidateAndUpdateLogFilepath() { + expectedCurrentLogFilepath := l.getLogFilepath() + if l.CurrentLogFile != expectedCurrentLogFilepath { + //Change of month. Update to a new log file + l.file.Close() + f, err := os.OpenFile(expectedCurrentLogFilepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) + if err != nil { + log.Println("[Logger] Unable to create new log. Logging to file disabled.") + l.LogToFile = false + return + } + l.CurrentLogFile = expectedCurrentLogFilepath + l.file = f + } +} + +func (l *Logger) Close() { + l.file.Close() +} diff --git a/src/mod/info/logviewer/logviewer.go b/src/mod/info/logviewer/logviewer.go new file mode 100644 index 0000000..24f0e19 --- /dev/null +++ b/src/mod/info/logviewer/logviewer.go @@ -0,0 +1,122 @@ +package logviewer + +import ( + "encoding/json" + "errors" + "io/fs" + "net/http" + "os" + "path/filepath" + "strings" + + "imuslab.com/zoraxy/mod/utils" +) + +type ViewerOption struct { + RootFolder string //The root folder to scan for log + Extension string //The extension the root files use, include the . in your ext (e.g. .log) +} + +type Viewer struct { + option *ViewerOption +} + +type LogFile struct { + Title string + Filename string + Fullpath string + Filesize int64 +} + +func NewLogViewer(option *ViewerOption) *Viewer { + return &Viewer{option: option} +} + +/* + Log Request Handlers +*/ +//List all the log files in the log folder. Return in map[string]LogFile format +func (v *Viewer) HandleListLog(w http.ResponseWriter, r *http.Request) { + logFiles := v.ListLogFiles(false) + js, _ := json.Marshal(logFiles) + utils.SendJSONResponse(w, string(js)) +} + +// Read log of a given catergory and filename +// Require GET varaible: file and catergory +func (v *Viewer) HandleReadLog(w http.ResponseWriter, r *http.Request) { + filename, err := utils.GetPara(r, "file") + if err != nil { + utils.SendErrorResponse(w, "invalid filename given") + return + } + + catergory, err := utils.GetPara(r, "catergory") + if err != nil { + utils.SendErrorResponse(w, "invalid catergory given") + return + } + + content, err := v.LoadLogFile(strings.TrimSpace(filepath.Base(catergory)), strings.TrimSpace(filepath.Base(filename))) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + utils.SendTextResponse(w, content) +} + +/* + Log Access Functions +*/ + +func (v *Viewer) ListLogFiles(showFullpath bool) map[string][]*LogFile { + result := map[string][]*LogFile{} + filepath.WalkDir(v.option.RootFolder, func(path string, di fs.DirEntry, err error) error { + if filepath.Ext(path) == v.option.Extension { + catergory := filepath.Base(filepath.Dir(path)) + logList, ok := result[catergory] + if !ok { + //this catergory hasn't been scanned before. + logList = []*LogFile{} + } + + fullpath := filepath.ToSlash(path) + if !showFullpath { + fullpath = "" + } + + st, err := os.Stat(path) + if err != nil { + return nil + } + + logList = append(logList, &LogFile{ + Title: strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)), + Filename: filepath.Base(path), + Fullpath: fullpath, + Filesize: st.Size(), + }) + + result[catergory] = logList + } + + return nil + }) + return result +} + +func (v *Viewer) LoadLogFile(catergory string, filename string) (string, error) { + logFilepath := filepath.Join(v.option.RootFolder, catergory, filename) + if utils.FileExists(logFilepath) { + //Load it + content, err := os.ReadFile(logFilepath) + if err != nil { + return "", err + } + + return string(content), nil + } else { + return "", errors.New("log file not found") + } +} diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 43c66de..f1bd381 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "log" "net/http" "path/filepath" "sort" @@ -25,33 +24,43 @@ func ReverseProxtInit() { inboundPort := 80 if sysdb.KeyExists("settings", "inbound") { sysdb.Read("settings", "inbound", &inboundPort) - log.Println("Serving inbound port ", inboundPort) + SystemWideLogger.Println("Serving inbound port ", inboundPort) } else { - log.Println("Inbound port not set. Using default (80)") + SystemWideLogger.Println("Inbound port not set. Using default (80)") } useTls := false sysdb.Read("settings", "usetls", &useTls) if useTls { - log.Println("TLS mode enabled. Serving proxxy request with TLS") + SystemWideLogger.Println("TLS mode enabled. Serving proxxy request with TLS") } else { - log.Println("TLS mode disabled. Serving proxy request with plain http") + SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http") } forceLatestTLSVersion := false sysdb.Read("settings", "forceLatestTLS", &forceLatestTLSVersion) if forceLatestTLSVersion { - log.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2") + SystemWideLogger.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2") } else { - log.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0") + SystemWideLogger.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0") + } + + listenOnPort80 := false + sysdb.Read("settings", "listenP80", &listenOnPort80) + if listenOnPort80 { + SystemWideLogger.Println("Port 80 listener enabled") + } else { + SystemWideLogger.Println("Port 80 listener disabled") } forceHttpsRedirect := false sysdb.Read("settings", "redirect", &forceHttpsRedirect) if forceHttpsRedirect { - log.Println("Force HTTPS mode enabled") + SystemWideLogger.Println("Force HTTPS mode enabled") + //Port 80 listener must be enabled to perform http -> https redirect + listenOnPort80 = true } else { - log.Println("Force HTTPS mode disabled") + SystemWideLogger.Println("Force HTTPS mode disabled") } dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{ @@ -59,6 +68,7 @@ func ReverseProxtInit() { Port: inboundPort, UseTls: useTls, ForceTLSLatest: forceLatestTLSVersion, + ListenOnPort80: listenOnPort80, ForceHttpsRedirect: forceHttpsRedirect, TlsManager: tlsCertManager, RedirectRuleTable: redirectTable, @@ -67,7 +77,7 @@ func ReverseProxtInit() { WebDirectory: *staticWebServerRoot, }) if err != nil { - log.Println(err.Error()) + SystemWideLogger.PrintAndLog("Proxy", "Unable to create dynamic proxy router", err) return } @@ -78,7 +88,7 @@ func ReverseProxtInit() { for _, conf := range confs { record, err := LoadReverseProxyConfig(conf) if err != nil { - log.Println("Failed to load "+filepath.Base(conf), err.Error()) + SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err) return } @@ -92,6 +102,7 @@ func ReverseProxtInit() { MatchingDomain: record.Rootname, Domain: record.ProxyTarget, RequireTLS: record.UseTLS, + BypassGlobalTLS: record.BypassGlobalTLS, SkipCertValidations: record.SkipTlsValidation, RequireBasicAuth: record.RequireBasicAuth, BasicAuthCredentials: record.BasicAuthCredentials, @@ -102,13 +113,14 @@ func ReverseProxtInit() { RootName: record.Rootname, Domain: record.ProxyTarget, RequireTLS: record.UseTLS, + BypassGlobalTLS: record.BypassGlobalTLS, SkipCertValidations: record.SkipTlsValidation, RequireBasicAuth: record.RequireBasicAuth, BasicAuthCredentials: record.BasicAuthCredentials, BasicAuthExceptionRules: record.BasicAuthExceptionRules, }) } else { - log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf)) + SystemWideLogger.PrintAndLog("Proxy", "Unsupported endpoint type: "+record.ProxyType+". Skipping "+filepath.Base(conf), nil) } } @@ -117,7 +129,7 @@ func ReverseProxtInit() { //reverse proxy server in front of this service time.Sleep(300 * time.Millisecond) dynamicProxyRouter.StartProxyService() - log.Println("Dynamic Reverse Proxy service started") + SystemWideLogger.Println("Dynamic Reverse Proxy service started") //Add all proxy services to uptime monitor //Create a uptime monitor service @@ -128,7 +140,7 @@ func ReverseProxtInit() { Interval: 300, //5 minutes MaxRecordsStore: 288, //1 day }) - log.Println("Uptime Monitor background service started") + SystemWideLogger.Println("Uptime Monitor background service started") }() } @@ -180,6 +192,13 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { useTLS := (tls == "true") + bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS") + if bypassGlobalTLS == "" { + bypassGlobalTLS = "false" + } + + useBypassGlobalTLS := bypassGlobalTLS == "true" + stv, _ := utils.PostPara(r, "tlsval") if stv == "" { stv = "false" @@ -240,6 +259,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { RootName: vdir, Domain: endpoint, RequireTLS: useTLS, + BypassGlobalTLS: useBypassGlobalTLS, SkipCertValidations: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: basicAuthCredentials, @@ -257,6 +277,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { MatchingDomain: subdomain, Domain: endpoint, RequireTLS: useTLS, + BypassGlobalTLS: useBypassGlobalTLS, SkipCertValidations: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: basicAuthCredentials, @@ -281,6 +302,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) { Rootname: rootname, ProxyTarget: endpoint, UseTLS: useTLS, + BypassGlobalTLS: useBypassGlobalTLS, SkipTlsValidation: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: basicAuthCredentials, @@ -332,9 +354,15 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { if stv == "" { stv = "false" } - skipTlsValidation := (stv == "true") + //Load bypass TLS option + bpgtls, _ := utils.PostPara(r, "bpgtls") + if bpgtls == "" { + bpgtls = "false" + } + bypassGlobalTLS := (bpgtls == "true") + rba, _ := utils.PostPara(r, "bauth") if rba == "" { rba = "false" @@ -354,6 +382,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { RootName: targetProxyEntry.RootOrMatchingDomain, Domain: endpoint, RequireTLS: useTLS, + BypassGlobalTLS: false, SkipCertValidations: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, @@ -366,6 +395,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { MatchingDomain: targetProxyEntry.RootOrMatchingDomain, Domain: endpoint, RequireTLS: useTLS, + BypassGlobalTLS: bypassGlobalTLS, SkipCertValidations: skipTlsValidation, RequireBasicAuth: requireBasicAuth, BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, @@ -385,6 +415,10 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) { BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials, } SaveReverseProxyConfigToFile(&thisProxyConfigRecord) + + //Update uptime monitor + UpdateUptimeMonitorTargets() + utils.SendOK(w) } @@ -417,6 +451,9 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) { uptimeMonitor.CleanRecords() } + //Update uptime monitor + UpdateUptimeMonitorTargets() + utils.SendOK(w) } @@ -731,6 +768,35 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) { } } +// Handle port 80 incoming traffics +func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) { + enabled, err := utils.GetPara(r, "enable") + if err != nil { + //Load the current status + currentEnabled := false + err = sysdb.Read("settings", "listenP80", ¤tEnabled) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + js, _ := json.Marshal(currentEnabled) + utils.SendJSONResponse(w, string(js)) + } else { + if enabled == "true" { + sysdb.Write("settings", "listenP80", true) + SystemWideLogger.Println("Enabling port 80 listener") + dynamicProxyRouter.UpdatePort80ListenerState(true) + } else if enabled == "false" { + sysdb.Write("settings", "listenP80", false) + SystemWideLogger.Println("Disabling port 80 listener") + dynamicProxyRouter.UpdatePort80ListenerState(true) + } else { + utils.SendErrorResponse(w, "invalid mode given: "+enabled) + } + utils.SendOK(w) + } +} + // Handle https redirect func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) { useRedirect, err := utils.GetPara(r, "set") @@ -751,11 +817,11 @@ func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) { } if useRedirect == "true" { sysdb.Write("settings", "redirect", true) - log.Println("Updating force HTTPS redirection to true") + SystemWideLogger.Println("Updating force HTTPS redirection to true") dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true) } else if useRedirect == "false" { sysdb.Write("settings", "redirect", false) - log.Println("Updating force HTTPS redirection to false") + SystemWideLogger.Println("Updating force HTTPS redirection to false") dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false) } diff --git a/src/start.go b/src/start.go index 4dd4aa8..c7da3b7 100644 --- a/src/start.go +++ b/src/start.go @@ -14,6 +14,7 @@ import ( "imuslab.com/zoraxy/mod/dynamicproxy/redirection" "imuslab.com/zoraxy/mod/ganserv" "imuslab.com/zoraxy/mod/geodb" + "imuslab.com/zoraxy/mod/info/logger" "imuslab.com/zoraxy/mod/mdns" "imuslab.com/zoraxy/mod/netstat" "imuslab.com/zoraxy/mod/pathrule" @@ -93,10 +94,17 @@ func startupSequence() { panic(err) } + //Create a system wide logger + l, err := logger.NewLogger("zr", "./log", *logOutputToFile) + if err == nil { + SystemWideLogger = l + } else { + panic(err) + } //Create a netstat buffer netstatBuffers, err = netstat.NewNetStatBuffer(300) if err != nil { - log.Println("Failed to load network statistic info") + SystemWideLogger.PrintAndLog("Network", "Failed to load network statistic info", err) panic(err) } @@ -134,13 +142,13 @@ func startupSequence() { BuildVersion: version, }, "") if err != nil { - log.Println("Unable to startup mDNS service. Disabling mDNS services") + SystemWideLogger.Println("Unable to startup mDNS service. Disabling mDNS services") } else { //Start initial scanning go func() { hosts := mdnsScanner.Scan(30, "") previousmdnsScanResults = hosts - log.Println("mDNS Startup scan completed") + SystemWideLogger.Println("mDNS Startup scan completed") }() //Create a ticker to update mDNS results every 5 minutes @@ -154,7 +162,7 @@ func startupSequence() { case <-ticker.C: hosts := mdnsScanner.Scan(30, "") previousmdnsScanResults = hosts - log.Println("mDNS scan result updated") + SystemWideLogger.Println("mDNS scan result updated") } } }() @@ -171,7 +179,7 @@ func startupSequence() { if usingZtAuthToken == "" { usingZtAuthToken, err = ganserv.TryLoadorAskUserForAuthkey() if err != nil { - log.Println("Failed to load ZeroTier controller API authtoken") + SystemWideLogger.Println("Failed to load ZeroTier controller API authtoken") } } ganManager = ganserv.NewNetworkManager(&ganserv.NetworkManagerOptions{ @@ -220,7 +228,7 @@ func startupSequence() { staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{ Sysdb: sysdb, - Port: "8081", //Default Port + Port: "5487", //Default Port WebRoot: *staticWebServerRoot, EnableDirectoryListing: true, EnableWebDirManager: *allowWebFileManager, diff --git a/src/web/components/rproot.html b/src/web/components/rproot.html index 6e0bfc7..a1ed65a 100644 --- a/src/web/components/rproot.html +++ b/src/web/components/rproot.html @@ -75,13 +75,13 @@
http://127.0.0.1:8081
in proxy rules to access the web server
diff --git a/src/wrappers.go b/src/wrappers.go
index f3d6fde..9fd7531 100644
--- a/src/wrappers.go
+++ b/src/wrappers.go
@@ -19,7 +19,6 @@ package main
import (
"encoding/json"
"fmt"
- "log"
"net/http"
"strings"
"time"
@@ -104,7 +103,21 @@ func HandleCountryDistrSummary(w http.ResponseWriter, r *http.Request) {
/*
Up Time Monitor
*/
-//Generate uptime monitor targets from reverse proxy rules
+
+// Update uptime monitor targets after rules updated
+// See https://github.com/tobychui/zoraxy/issues/77
+func UpdateUptimeMonitorTargets() {
+ if uptimeMonitor != nil {
+ uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
+ go func() {
+ uptimeMonitor.ExecuteUptimeCheck()
+ }()
+
+ SystemWideLogger.PrintAndLog("Uptime", "Uptime monitor config updated", nil)
+ }
+}
+
+// Generate uptime monitor targets from reverse proxy rules
func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
subds := dp.GetSDProxyEndpointsAsMap()
vdirs := dp.GetVDProxyEndpointsAsMap()
@@ -263,7 +276,7 @@ func HandleWakeOnLan(w http.ResponseWriter, r *http.Request) {
return
}
- log.Println("[WoL] Sending Wake on LAN magic packet to " + wake)
+ SystemWideLogger.PrintAndLog("WoL", "Sending Wake on LAN magic packet to "+wake, nil)
err := wakeonlan.WakeTarget(wake)
if err != nil {
utils.SendErrorResponse(w, err.Error())