From 70adadf129c71774ac3f7c66d495934e7763e033 Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Thu, 17 Aug 2023 14:08:48 +0800 Subject: [PATCH 01/10] bug fixes for 2.6.5 + Added potential fixes for #34 and #39 + Added better error handling for #43 --- src/main.go | 10 ++-- src/mod/dynamicproxy/dpcore/utils.go | 9 ++++ src/mod/mdns/mdns.go | 2 +- src/reverseproxy.go | 7 +++ src/start.go | 76 ++++++++++++++-------------- src/web/tools/mdns.html | 7 +++ src/wrappers.go | 8 +++ 7 files changed, 78 insertions(+), 41 deletions(-) diff --git a/src/main.go b/src/main.go index 8bbac8d..96d7d3f 100644 --- a/src/main.go +++ b/src/main.go @@ -36,6 +36,7 @@ import ( var noauth = flag.Bool("noauth", false, "Disable authentication for management interface") var showver = flag.Bool("version", false, "Show version of this server") var allowSshLoopback = flag.Bool("sshlb", false, "Allow loopback web ssh connection (DANGER)") +var allowMdnsScanning = flag.Bool("mdns", true, "Enable mDNS scanner and transponder") var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local node") var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port") var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)") @@ -96,9 +97,12 @@ func ShutdownSeq() { netstatBuffers.Close() fmt.Println("- Closing Statistic Collector") statisticCollector.Close() - fmt.Println("- Stopping mDNS Discoverer") - //Stop the mdns service - mdnsTickerStop <- true + if mdnsTickerStop != nil { + fmt.Println("- Stopping mDNS Discoverer") + // Stop the mdns service + mdnsTickerStop <- true + } + mdnsScanner.Close() fmt.Println("- Closing Certificates Auto Renewer") acmeAutoRenewer.Close() diff --git a/src/mod/dynamicproxy/dpcore/utils.go b/src/mod/dynamicproxy/dpcore/utils.go index 12c7f78..7f99fb4 100644 --- a/src/mod/dynamicproxy/dpcore/utils.go +++ b/src/mod/dynamicproxy/dpcore/utils.go @@ -21,6 +21,15 @@ func replaceLocationHost(urlString string, rrr *ResponseRewriteRuleSet, useTLS b u.Scheme = "http" } + //Issue #39: Check if it is location target match the proxying domain + //E.g. Proxy config: blog.example.com -> example.com/blog + //Check if it is actually redirecting to example.com instead of a new domain + //like news.example.com. + if rrr.ProxyDomain != u.Host { + //New location domain not matching proxy target domain. + //Do not modify location header + return urlString, nil + } u.Host = rrr.OriginalHost if strings.Contains(rrr.ProxyDomain, "/") { diff --git a/src/mod/mdns/mdns.go b/src/mod/mdns/mdns.go index 3a0022d..f266029 100644 --- a/src/mod/mdns/mdns.go +++ b/src/mod/mdns/mdns.go @@ -226,7 +226,7 @@ func (m *MDNSHost) Scan(timeout int, domainFilter string) []*NetworkHost { return discoveredHost } -//Get all mac address of all interfaces +// Get all mac address of all interfaces func getMacAddr() ([]string, error) { ifas, err := net.Interfaces() if err != nil { diff --git a/src/reverseproxy.go b/src/reverseproxy.go index a5f8159..000653c 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -649,6 +649,13 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) { } //Check if it is identical as proxy root (recursion!) + if dynamicProxyRouter.Root == nil || dynamicProxyRouter.Root.Domain == "" { + //Check if proxy root is set before checking recursive listen + //Fixing issue #43 + utils.SendErrorResponse(w, "Proxy root not set") + return + } + proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/") if strings.HasPrefix(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.HasPrefix(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) { //Listening port is same as proxy root diff --git a/src/start.go b/src/start.go index 30d5eb2..0cf999c 100644 --- a/src/start.go +++ b/src/start.go @@ -114,47 +114,49 @@ func startupSequence() { This discover nearby ArozOS Nodes or other services that provide mDNS discovery with domain (e.g. Synology NAS) */ - portInt, err := strconv.Atoi(strings.Split(handler.Port, ":")[1]) - if err != nil { - portInt = 8000 - } - mdnsScanner, err = mdns.NewMDNS(mdns.NetworkHost{ - HostName: "zoraxy_" + nodeUUID, - Port: portInt, - Domain: "zoraxy.imuslab.com", - Model: "Network Gateway", - UUID: nodeUUID, - Vendor: "imuslab.com", - BuildVersion: version, - }, "") - if err != nil { - log.Println("Unable to startup mDNS service.") - log.Fatal(err) - } - //Start initial scanning - go func() { - hosts := mdnsScanner.Scan(30, "") - previousmdnsScanResults = hosts - log.Println("mDNS Startup scan completed") - }() - - //Create a ticker to update mDNS results every 5 minutes - ticker := time.NewTicker(15 * time.Minute) - stopChan := make(chan bool) - go func() { - for { - select { - case <-stopChan: - ticker.Stop() - case <-ticker.C: + if *allowMdnsScanning { + portInt, err := strconv.Atoi(strings.Split(handler.Port, ":")[1]) + if err != nil { + portInt = 8000 + } + mdnsScanner, err = mdns.NewMDNS(mdns.NetworkHost{ + HostName: "zoraxy_" + nodeUUID, + Port: portInt, + Domain: "zoraxy.arozos.com", + Model: "Network Gateway", + UUID: nodeUUID, + Vendor: "imuslab.com", + BuildVersion: version, + }, "") + if err != nil { + log.Println("Unable to startup mDNS service. Disabling mDNS services") + } else { + //Start initial scanning + go func() { hosts := mdnsScanner.Scan(30, "") previousmdnsScanResults = hosts - log.Println("mDNS scan result updated") - } + log.Println("mDNS Startup scan completed") + }() + + //Create a ticker to update mDNS results every 5 minutes + ticker := time.NewTicker(15 * time.Minute) + stopChan := make(chan bool) + go func() { + for { + select { + case <-stopChan: + ticker.Stop() + case <-ticker.C: + hosts := mdnsScanner.Scan(30, "") + previousmdnsScanResults = hosts + log.Println("mDNS scan result updated") + } + } + }() + mdnsTickerStop = stopChan } - }() - mdnsTickerStop = stopChan + } /* Global Area Network diff --git a/src/web/tools/mdns.html b/src/web/tools/mdns.html index 2dc63e8..2044e30 100644 --- a/src/web/tools/mdns.html +++ b/src/web/tools/mdns.html @@ -96,6 +96,13 @@

Scan with custom domain filter

$('').text('Vendor') ) ); + + if (data.error != undefined){ + $('#mdns-hosts').html(`
`); + $('#mdns-hosts').find("tbody").append(` ${data.error}`); + $("#discover").addClass("disabled"); + return; + } // Create table body var tableBody = $(''); diff --git a/src/wrappers.go b/src/wrappers.go index cf95cd9..77c4569 100644 --- a/src/wrappers.go +++ b/src/wrappers.go @@ -156,11 +156,19 @@ func HandleUptimeMonitorListing(w http.ResponseWriter, r *http.Request) { // Handle listing current registered mdns nodes func HandleMdnsListing(w http.ResponseWriter, r *http.Request) { + if mdnsScanner == nil { + utils.SendErrorResponse(w, "mDNS scanner is disabled on this host") + return + } js, _ := json.Marshal(previousmdnsScanResults) utils.SendJSONResponse(w, string(js)) } func HandleMdnsScanning(w http.ResponseWriter, r *http.Request) { + if mdnsScanner == nil { + utils.SendErrorResponse(w, "mDNS scanner is disabled on this host") + return + } domain, err := utils.PostPara(r, "domain") var hosts []*mdns.NetworkHost if err != nil { From a3d55a3274f28d4d714407cb4669cf232c7ce78c Mon Sep 17 00:00:00 2001 From: Toby Chui Date: Sun, 20 Aug 2023 14:50:25 +0800 Subject: [PATCH 02/10] Patching redirection bug + Added wip basic auth editor custom exception rules + Added custom logic to handle apache screw up redirect header --- src/main.go | 2 +- src/mod/dynamicproxy/dpcore/utils.go | 4 +++- src/reverseproxy.go | 4 ++-- src/web/components/rules.html | 3 ++- src/web/snippet/basicAuthEditor.html | 29 +++++++++++++++++++++++++++- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/main.go b/src/main.go index 96d7d3f..434ce57 100644 --- a/src/main.go +++ b/src/main.go @@ -44,7 +44,7 @@ var ( name = "Zoraxy" version = "2.6.6" nodeUUID = "generic" - development = false //Set this to false to use embedded web fs + development = true //Set this to false to use embedded web fs bootTime = time.Now().Unix() /* diff --git a/src/mod/dynamicproxy/dpcore/utils.go b/src/mod/dynamicproxy/dpcore/utils.go index 7f99fb4..27459b6 100644 --- a/src/mod/dynamicproxy/dpcore/utils.go +++ b/src/mod/dynamicproxy/dpcore/utils.go @@ -25,7 +25,9 @@ func replaceLocationHost(urlString string, rrr *ResponseRewriteRuleSet, useTLS b //E.g. Proxy config: blog.example.com -> example.com/blog //Check if it is actually redirecting to example.com instead of a new domain //like news.example.com. - if rrr.ProxyDomain != u.Host { + // The later check bypass apache screw up method of redirection header + // e.g. https://imuslab.com -> http://imuslab.com:443 + if rrr.ProxyDomain != u.Host && !strings.Contains(u.Host, rrr.OriginalHost+":") { //New location domain not matching proxy target domain. //Do not modify location header return urlString, nil diff --git a/src/reverseproxy.go b/src/reverseproxy.go index 000653c..1e032a4 100644 --- a/src/reverseproxy.go +++ b/src/reverseproxy.go @@ -644,7 +644,7 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) { newIncomingPortInt, err := strconv.Atoi(newIncomingPort) if err != nil { - utils.SendErrorResponse(w, "invalid incoming port given") + utils.SendErrorResponse(w, "Invalid incoming port given") return } @@ -652,7 +652,7 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) { if dynamicProxyRouter.Root == nil || dynamicProxyRouter.Root.Domain == "" { //Check if proxy root is set before checking recursive listen //Fixing issue #43 - utils.SendErrorResponse(w, "Proxy root not set") + utils.SendErrorResponse(w, "Set Proxy Root before changing inbound port") return } diff --git a/src/web/components/rules.html b/src/web/components/rules.html index b70a6ab..a952ed8 100644 --- a/src/web/components/rules.html +++ b/src/web/components/rules.html @@ -377,7 +377,8 @@

New Proxy Rule

column.empty().append(`
-
`); + + `); }else if (datatype == 'action'){ column.empty().append(` diff --git a/src/web/snippet/basicAuthEditor.html b/src/web/snippet/basicAuthEditor.html index f9934d9..4b80546 100644 --- a/src/web/snippet/basicAuthEditor.html +++ b/src/web/snippet/basicAuthEditor.html @@ -11,10 +11,12 @@
- Basic Auth Credential + Basic Auth Settings
+
+

Basic Auth Credential

Enter the username and password for allowing them to access this proxy endpoint

@@ -49,6 +51,31 @@
+
+

No-Auth Paths

+
+

Exclude specific paths from the basic auth interface. Useful if you are hosting services require remote API access.

+ + + + + + + + + + + + +
UsernamePasswordRemove
No Path Excluded
+
+ +
+
+ +
+
+ \ No newline at end of file diff --git a/src/wrappers.go b/src/wrappers.go index 77c4569..f3d6fde 100644 --- a/src/wrappers.go +++ b/src/wrappers.go @@ -335,3 +335,20 @@ func HandleZoraxyInfo(w http.ResponseWriter, r *http.Request) { js, _ := json.MarshalIndent(info, "", " ") utils.SendJSONResponse(w, string(js)) } + +func HandleGeoIpLookup(w http.ResponseWriter, r *http.Request) { + ip, err := utils.GetPara(r, "ip") + if err != nil { + utils.SendErrorResponse(w, "ip not given") + return + } + + cc, err := geodbStore.ResolveCountryCodeFromIP(ip) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + js, _ := json.Marshal(cc) + utils.SendJSONResponse(w, string(js)) +} From 6a0c7cf4991214701f1b4ecb8f2c06d08ae188d0 Mon Sep 17 00:00:00 2001 From: dalun Date: Mon, 28 Aug 2023 03:31:33 +0000 Subject: [PATCH 08/10] add skipTLS for custom acme server --- src/mod/acme/acme.go | 35 +++++++++++++++++++++++++++++++++-- src/mod/acme/autorenew.go | 2 +- src/web/snippet/acme.html | 11 +++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/mod/acme/acme.go b/src/mod/acme/acme.go index 1178168..eba1d98 100644 --- a/src/mod/acme/acme.go +++ b/src/mod/acme/acme.go @@ -5,6 +5,7 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/tls" "crypto/x509" "encoding/json" "encoding/pem" @@ -30,6 +31,7 @@ import ( type CertificateInfoJSON struct { AcmeName string `json:"acme_name"` AcmeUrl string `json:"acme_url"` + SkipTLS bool `json:"skip_tls"` } // ACMEUser represents a user in the ACME system. @@ -69,7 +71,7 @@ func NewACME(acmeServer string, port string) *ACMEHandler { } // ObtainCert obtains a certificate for the specified domains. -func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string) (bool, error) { +func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool) (bool, error) { log.Println("[ACME] Obtaining certificate...") // generate private key @@ -88,6 +90,24 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email // create config config := lego.NewConfig(&adminUser) + // skip TLS verify if need + // Ref: https://github.com/go-acme/lego/blob/6af2c756ac73a9cb401621afca722d0f4112b1b8/lego/client_config.go#L74 + if skipTLS { + log.Println("[INFO] Ignore TLS/SSL Verification Error for ACME Server") + config.HTTPClient.Transport = &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + TLSHandshakeTimeout: 30 * time.Second, + ResponseHeaderTimeout: 30 * time.Second, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + } + // setup the custom ACME url endpoint. if caUrl != "" { config.CADirURL = caUrl @@ -159,6 +179,7 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email certInfo := &CertificateInfoJSON{ AcmeName: caName, AcmeUrl: caUrl, + SkipTLS: skipTLS, } certInfoBytes, err := json.Marshal(certInfo) @@ -294,8 +315,18 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ } } + var skipTLS bool + + if skipTLSString, err := utils.PostPara(r, "skipTLS"); err != nil { + skipTLS = false + } else if skipTLSString != "true" { + skipTLS = false + } else { + skipTLS = true + } + domains := strings.Split(domainPara, ",") - result, err := a.ObtainCert(domains, filename, email, ca, caUrl) + result, err := a.ObtainCert(domains, filename, email, ca, caUrl, skipTLS) if err != nil { utils.SendErrorResponse(w, jsonEscape(err.Error())) return diff --git a/src/mod/acme/autorenew.go b/src/mod/acme/autorenew.go index 6ed7433..13872fd 100644 --- a/src/mod/acme/autorenew.go +++ b/src/mod/acme/autorenew.go @@ -365,7 +365,7 @@ func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, erro certInfo = &CertificateInfoJSON{} } - _, err = a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, certInfo.AcmeName, certInfo.AcmeUrl) + _, err = a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, certInfo.AcmeName, certInfo.AcmeUrl, certInfo.SkipTLS) if err != nil { log.Println("Renew " + fileName + "(" + strings.Join(expiredCert.Domains, ",") + ") failed: " + err.Error()) } else { diff --git a/src/web/snippet/acme.html b/src/web/snippet/acme.html index 24e1d8c..08f6278 100644 --- a/src/web/snippet/acme.html +++ b/src/web/snippet/acme.html @@ -118,6 +118,12 @@

Generate New Certificate

+
@@ -303,8 +309,10 @@

Generate New Certificate

$("input[name=ca]").on('change', function() { if(this.value == "Custom ACME Server") { $("#customca").show(); + $("#skipTLS").show(); } else { $("#customca").hide(); + $("#skipTLS").hide(); } }) @@ -337,6 +345,8 @@

Generate New Certificate

ca_url = $("#caurl").val(); } + var skipTLSValue = $("#skipTLSCheckbox")[0].checked; + $.ajax({ url: "/api/acme/obtainCert", method: "GET", @@ -346,6 +356,7 @@

Generate New Certificate

email: email, ca: ca, ca_url: ca_url, + skipTLS: skipTLSValue, }, success: function(response) { $("#obtainButton").removeClass("loading").removeClass("disabled"); From eb98624a6ac3ada1cdf4159021606f38d630d5d9 Mon Sep 17 00:00:00 2001 From: dalun Date: Mon, 28 Aug 2023 03:35:20 +0000 Subject: [PATCH 09/10] fix naming convention --- src/mod/acme/acme.go | 2 +- src/web/snippet/acme.html | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mod/acme/acme.go b/src/mod/acme/acme.go index eba1d98..d82cf05 100644 --- a/src/mod/acme/acme.go +++ b/src/mod/acme/acme.go @@ -308,7 +308,7 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ } if ca == "custom" { - caUrl, err = utils.PostPara(r, "ca_url") + caUrl, err = utils.PostPara(r, "caURL") if err != nil { log.Println("Custom CA set but no URL provide, Using default") ca, caUrl = "", "" diff --git a/src/web/snippet/acme.html b/src/web/snippet/acme.html index 08f6278..554fb24 100644 --- a/src/web/snippet/acme.html +++ b/src/web/snippet/acme.html @@ -114,9 +114,9 @@

Generate New Certificate

-