Optimized UX and code structure

+ Added automatic self-sign certificate sniffing
+ Moved all constant into def.go
+ Added auto restart on port change when proxy server is running
+ Optimized slow search geoIP resolver by introducing new cache mechanism
+ Updated default incoming port to HTTPS instead of HTTP
This commit is contained in:
Toby Chui 2024-11-24 11:38:01 +08:00
parent 0af8c67346
commit 015889851a
16 changed files with 249 additions and 115 deletions

View File

@ -8,6 +8,7 @@ import (
"imuslab.com/zoraxy/mod/acme/acmedns" "imuslab.com/zoraxy/mod/acme/acmedns"
"imuslab.com/zoraxy/mod/acme/acmewizard" "imuslab.com/zoraxy/mod/acme/acmewizard"
"imuslab.com/zoraxy/mod/auth" "imuslab.com/zoraxy/mod/auth"
"imuslab.com/zoraxy/mod/dynamicproxy/domainsniff"
"imuslab.com/zoraxy/mod/ipscan" "imuslab.com/zoraxy/mod/ipscan"
"imuslab.com/zoraxy/mod/netstat" "imuslab.com/zoraxy/mod/netstat"
"imuslab.com/zoraxy/mod/netutils" "imuslab.com/zoraxy/mod/netutils"
@ -33,7 +34,7 @@ func RegisterHTTPProxyAPIs(authRouter *auth.RouterDef) {
authRouter.HandleFunc("/api/proxy/setAlias", ReverseProxyHandleAlias) authRouter.HandleFunc("/api/proxy/setAlias", ReverseProxyHandleAlias)
authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint) authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
authRouter.HandleFunc("/api/proxy/updateCredentials", UpdateProxyBasicAuthCredentials) authRouter.HandleFunc("/api/proxy/updateCredentials", UpdateProxyBasicAuthCredentials)
authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS) authRouter.HandleFunc("/api/proxy/tlscheck", domainsniff.HandleCheckSiteSupportTLS)
authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet) authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect) authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener) authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener)

View File

@ -177,7 +177,10 @@ func handleListDomains(w http.ResponseWriter, r *http.Request) {
// Handle front-end toggling TLS mode // Handle front-end toggling TLS mode
func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) { func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) {
currentTlsSetting := false currentTlsSetting := true //Default to true
if dynamicProxyRouter.Option != nil {
currentTlsSetting = dynamicProxyRouter.Option.UseTls
}
if sysdb.KeyExists("settings", "usetls") { if sysdb.KeyExists("settings", "usetls") {
sysdb.Read("settings", "usetls", &currentTlsSetting) sysdb.Read("settings", "usetls", &currentTlsSetting)
} }

View File

@ -43,7 +43,7 @@ const (
/* Build Constants */ /* Build Constants */
SYSTEM_NAME = "Zoraxy" SYSTEM_NAME = "Zoraxy"
SYSTEM_VERSION = "3.1.4" SYSTEM_VERSION = "3.1.4"
DEVELOPMENT_BUILD = true /* Development: Set to false to use embedded web fs */ DEVELOPMENT_BUILD = false /* Development: Set to false to use embedded web fs */
/* System Constants */ /* System Constants */
DATABASE_PATH = "sys.db" DATABASE_PATH = "sys.db"
@ -55,6 +55,7 @@ const (
MDNS_IDENTIFY_VENDOR = "imuslab.com" MDNS_IDENTIFY_VENDOR = "imuslab.com"
MDNS_SCAN_TIMEOUT = 30 /* Seconds */ MDNS_SCAN_TIMEOUT = 30 /* Seconds */
MDNS_SCAN_UPDATE_INTERVAL = 15 /* Minutes */ MDNS_SCAN_UPDATE_INTERVAL = 15 /* Minutes */
GEODB_CACHE_CLEAR_INTERVAL = 15 /* Minutes */
ACME_AUTORENEW_CONFIG_PATH = "./conf/acme_conf.json" ACME_AUTORENEW_CONFIG_PATH = "./conf/acme_conf.json"
CSRF_COOKIENAME = "zoraxy_csrf" CSRF_COOKIENAME = "zoraxy_csrf"
LOG_PREFIX = "zr" LOG_PREFIX = "zr"
@ -71,27 +72,29 @@ const (
) )
/* System Startup Flags */ /* System Startup Flags */
var webUIPort = flag.String("port", ":8000", "Management web interface listening port") var (
var noauth = flag.Bool("noauth", false, "Disable authentication for management interface") webUIPort = flag.String("port", ":8000", "Management web interface listening port")
var showver = flag.Bool("version", false, "Show version of this server") noauth = flag.Bool("noauth", false, "Disable authentication for management interface")
var allowSshLoopback = flag.Bool("sshlb", false, "Allow loopback web ssh connection (DANGER)") showver = flag.Bool("version", false, "Show version of this server")
var allowMdnsScanning = flag.Bool("mdns", true, "Enable mDNS scanner and transponder") allowSshLoopback = flag.Bool("sshlb", false, "Allow loopback web ssh connection (DANGER)")
var mdnsName = flag.String("mdnsname", "", "mDNS name, leave empty to use default (zoraxy_{node-uuid}.local)") allowMdnsScanning = flag.Bool("mdns", true, "Enable mDNS scanner and transponder")
var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local node") mdnsName = flag.String("mdnsname", "", "mDNS name, leave empty to use default (zoraxy_{node-uuid}.local)")
var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port") ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local node")
var runningInDocker = flag.Bool("docker", false, "Run Zoraxy in docker compatibility mode") ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)") runningInDocker = flag.Bool("docker", false, "Run Zoraxy in docker compatibility mode")
var acmeCertAutoRenewDays = flag.Int("earlyrenew", 30, "Number of days to early renew a soon expiring certificate (days)") acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
var enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)") acmeCertAutoRenewDays = flag.Int("earlyrenew", 30, "Number of days to early renew a soon expiring certificate (days)")
var staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters") enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
var allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder") staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters")
var enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected") allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected")
)
/* Global Variables and Handlers */ /* Global Variables and Handlers */
var ( var (
nodeUUID = "generic" //System uuid, in uuidv4 format, load from database on startup nodeUUID = "generic" //System uuid in uuidv4 format, load from database on startup
bootTime = time.Now().Unix() bootTime = time.Now().Unix()
requireAuth = true /* Require authentication for webmin panel */ requireAuth = true //Require authentication for webmin panel, override from flag
/* /*
Binary Embedding File System Binary Embedding File System
@ -131,5 +134,5 @@ var (
AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic
DockerUXOptimizer *dockerux.UXOptimizer //Docker user experience optimizer, community contribution only DockerUXOptimizer *dockerux.UXOptimizer //Docker user experience optimizer, community contribution only
SystemWideLogger *logger.Logger //Logger for Zoraxy SystemWideLogger *logger.Logger //Logger for Zoraxy
LogViewer *logviewer.Viewer LogViewer *logviewer.Viewer //Log viewer HTTP handlers
) )

View File

@ -210,8 +210,8 @@ func (a *AuthAgent) Logout(w http.ResponseWriter, r *http.Request) error {
} }
session.Values["authenticated"] = false session.Values["authenticated"] = false
session.Values["username"] = nil session.Values["username"] = nil
session.Save(r, w) session.Options.MaxAge = -1
return nil return session.Save(r, w)
} }
// Get the current session username from request // Get the current session username from request
@ -339,6 +339,7 @@ func (a *AuthAgent) CheckAuth(r *http.Request) bool {
if err != nil { if err != nil {
return false return false
} }
// Check if user is authenticated // Check if user is authenticated
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth { if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
return false return false

View File

@ -1,7 +1,6 @@
package dynamicproxy package dynamicproxy
import ( import (
"log"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -16,7 +15,7 @@ func (h *ProxyHandler) handleAccessRouting(ruleID string, w http.ResponseWriter,
accessRule, err := h.Parent.Option.AccessController.GetAccessRuleByID(ruleID) accessRule, err := h.Parent.Option.AccessController.GetAccessRuleByID(ruleID)
if err != nil { if err != nil {
//Unable to load access rule. Target rule not found? //Unable to load access rule. Target rule not found?
log.Println("[Proxy] Unable to load access rule: " + ruleID) h.Parent.Option.Logger.PrintAndLog("proxy-access", "Unable to load access rule: "+ruleID, err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 - Internal Server Error")) w.Write([]byte("500 - Internal Server Error"))
return true return true

View File

@ -10,8 +10,14 @@ package domainsniff
*/ */
import ( import (
"crypto/tls" "crypto/tls"
"encoding/json"
"fmt"
"net" "net"
"net/http"
"strings"
"time" "time"
"imuslab.com/zoraxy/mod/utils"
) )
// Check if the domain is reachable and return err if not reachable // Check if the domain is reachable and return err if not reachable
@ -27,30 +33,114 @@ func DomainReachableWithError(domain string) error {
} }
// Check if a domain have TLS but it is self-signed or expired // Check if a domain have TLS but it is self-signed or expired
func DomainIsSelfSigned(domain string) (bool, error) { // Return false if sniff error
func DomainIsSelfSigned(domain string) bool {
//Extract the domain from URl in case the user input the full URL
host, port, err := net.SplitHostPort(domain)
if err != nil {
host = domain
} else {
domain = host + ":" + port
}
if !strings.Contains(domain, ":") {
domain = domain + ":443"
}
//Get the certificate //Get the certificate
conn, err := net.Dial("tcp", domain) conn, err := net.Dial("tcp", domain)
if err != nil { if err != nil {
return false, err return false
} }
defer conn.Close() defer conn.Close()
//Connect with TLS using secure verify
tlsConn := tls.Client(conn, nil)
err = tlsConn.Handshake()
if err == nil {
//This is a valid certificate
fmt.Println()
return false
}
//Connect with TLS using insecure skip verify //Connect with TLS using insecure skip verify
config := &tls.Config{ config := &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
} }
tlsConn := tls.Client(conn, config) tlsConn = tls.Client(conn, config)
err = tlsConn.Handshake() err = tlsConn.Handshake()
if err != nil { //If the handshake is successful, this is a self-signed certificate
return false, err return err == nil
}
//Check if the certificate is self-signed
cert := tlsConn.ConnectionState().PeerCertificates[0]
return cert.Issuer.CommonName == cert.Subject.CommonName, nil
} }
// Check if domain reachable // Check if domain reachable
func DomainReachable(domain string) bool { func DomainReachable(domain string) bool {
return DomainReachableWithError(domain) == nil return DomainReachableWithError(domain) == nil
} }
// Check if domain is served by a web server using HTTPS
func DomainUsesTLS(targetURL string) bool {
//Check if the site support https
httpsUrl := fmt.Sprintf("https://%s", targetURL)
httpUrl := fmt.Sprintf("http://%s", targetURL)
client := http.Client{Timeout: 5 * time.Second}
resp, err := client.Head(httpsUrl)
if err == nil && resp.StatusCode == http.StatusOK {
return true
}
resp, err = client.Head(httpUrl)
if err == nil && resp.StatusCode == http.StatusOK {
return false
}
//If the site is not reachable, return false
return false
}
/*
Request Handlers
*/
//Check if site support TLS
//Pass in ?selfsignchk=true to also check for self-signed certificate
func HandleCheckSiteSupportTLS(w http.ResponseWriter, r *http.Request) {
targetURL, err := utils.PostPara(r, "url")
if err != nil {
utils.SendErrorResponse(w, "invalid url given")
return
}
//If the selfsign flag is set, also chec for self-signed certificate
_, err = utils.PostBool(r, "selfsignchk")
if err == nil {
//Return the https and selfsign status
type result struct {
Protocol string `json:"protocol"`
SelfSign bool `json:"selfsign"`
}
scanResult := result{Protocol: "http", SelfSign: false}
if DomainUsesTLS(targetURL) {
scanResult.Protocol = "https"
if DomainIsSelfSigned(targetURL) {
scanResult.SelfSign = true
}
}
js, _ := json.Marshal(scanResult)
utils.SendJSONResponse(w, string(js))
return
}
if DomainUsesTLS(targetURL) {
js, _ := json.Marshal("https")
utils.SendJSONResponse(w, string(js))
return
} else {
js, _ := json.Marshal("http")
utils.SendJSONResponse(w, string(js))
return
}
}

View File

@ -291,7 +291,7 @@ func (router *Router) Restart() error {
return err return err
} }
time.Sleep(300 * time.Millisecond) time.Sleep(800 * time.Millisecond)
// Start the server // Start the server
err = router.StartProxyService() err = router.StartProxyService()
if err != nil { if err != nil {

View File

@ -3,6 +3,7 @@ package geodb
import ( import (
_ "embed" _ "embed"
"net/http" "net/http"
"time"
"imuslab.com/zoraxy/mod/database" "imuslab.com/zoraxy/mod/database"
"imuslab.com/zoraxy/mod/netutils" "imuslab.com/zoraxy/mod/netutils"
@ -15,17 +16,22 @@ var geoipv4 []byte //Geodb dataset for ipv4
var geoipv6 []byte //Geodb dataset for ipv6 var geoipv6 []byte //Geodb dataset for ipv6
type Store struct { type Store struct {
geodb [][]string //Parsed geodb list geodb [][]string //Parsed geodb list
geodbIpv6 [][]string //Parsed geodb list for ipv6 geodbIpv6 [][]string //Parsed geodb list for ipv6
geotrie *trie geotrie *trie
geotrieIpv6 *trie geotrieIpv6 *trie
sysdb *database.Database sysdb *database.Database
option *StoreOptions slowLookupCacheIpv4 map[string]string //Cache for slow lookup
slowLookupCacheIpv6 map[string]string //Cache for slow lookup
cacheClearTicker *time.Ticker //Ticker for clearing cache
cacheClearTickerStopChan chan bool //Stop channel for cache clear ticker
option *StoreOptions
} }
type StoreOptions struct { type StoreOptions struct {
AllowSlowIpv4LookUp bool AllowSlowIpv4LookUp bool
AllowSloeIpv6Lookup bool AllowSlowIpv6Lookup bool
SlowLookupCacheClearInterval time.Duration //Clear slow lookup cache interval
} }
type CountryInfo struct { type CountryInfo struct {
@ -50,18 +56,44 @@ func NewGeoDb(sysdb *database.Database, option *StoreOptions) (*Store, error) {
} }
var ipv6Trie *trie var ipv6Trie *trie
if !option.AllowSloeIpv6Lookup { if !option.AllowSlowIpv6Lookup {
ipv6Trie = constrctTrieTree(parsedGeoDataIpv6) ipv6Trie = constrctTrieTree(parsedGeoDataIpv6)
} }
return &Store{ if option.SlowLookupCacheClearInterval == 0 {
geodb: parsedGeoData, option.SlowLookupCacheClearInterval = 15 * time.Minute
geotrie: ipv4Trie, }
geodbIpv6: parsedGeoDataIpv6,
geotrieIpv6: ipv6Trie, //Create a new store
sysdb: sysdb, thisGeoDBStore := &Store{
option: option, geodb: parsedGeoData,
}, nil geotrie: ipv4Trie,
geodbIpv6: parsedGeoDataIpv6,
geotrieIpv6: ipv6Trie,
sysdb: sysdb,
slowLookupCacheIpv4: make(map[string]string),
slowLookupCacheIpv6: make(map[string]string),
cacheClearTicker: time.NewTicker(option.SlowLookupCacheClearInterval),
cacheClearTickerStopChan: make(chan bool),
option: option,
}
//Start cache clear ticker
if option.AllowSlowIpv4LookUp || option.AllowSlowIpv6Lookup {
go func(store *Store) {
for {
select {
case <-store.cacheClearTickerStopChan:
return
case <-thisGeoDBStore.cacheClearTicker.C:
thisGeoDBStore.slowLookupCacheIpv4 = make(map[string]string)
thisGeoDBStore.slowLookupCacheIpv6 = make(map[string]string)
}
}
}(thisGeoDBStore)
}
return thisGeoDBStore, nil
} }
func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error) { func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error) {
@ -73,8 +105,12 @@ func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error)
} }
// Close the store
func (s *Store) Close() { func (s *Store) Close() {
if s.option.AllowSlowIpv4LookUp || s.option.AllowSlowIpv6Lookup {
//Stop cache clear ticker
s.cacheClearTickerStopChan <- true
}
} }
func (s *Store) GetRequesterCountryISOCode(r *http.Request) string { func (s *Store) GetRequesterCountryISOCode(r *http.Request) string {

View File

@ -44,6 +44,7 @@ func TestResolveCountryCodeFromIP(t *testing.T) {
store, err := geodb.NewGeoDb(nil, &geodb.StoreOptions{ store, err := geodb.NewGeoDb(nil, &geodb.StoreOptions{
false, false,
true, true,
0,
}) })
if err != nil { if err != nil {
t.Errorf("error creating store: %v", err) t.Errorf("error creating store: %v", err)

View File

@ -56,6 +56,12 @@ func (s *Store) slowSearchIpv4(ipAddr string) string {
if isReservedIP(ipAddr) { if isReservedIP(ipAddr) {
return "" return ""
} }
//Check if already in cache
if cc, ok := s.slowLookupCacheIpv4[ipAddr]; ok {
return cc
}
for _, ipRange := range s.geodb { for _, ipRange := range s.geodb {
startIp := ipRange[0] startIp := ipRange[0]
endIp := ipRange[1] endIp := ipRange[1]
@ -63,6 +69,8 @@ func (s *Store) slowSearchIpv4(ipAddr string) string {
inRange, _ := isIPv4InRange(startIp, endIp, ipAddr) inRange, _ := isIPv4InRange(startIp, endIp, ipAddr)
if inRange { if inRange {
//Add to cache
s.slowLookupCacheIpv4[ipAddr] = cc
return cc return cc
} }
} }
@ -73,6 +81,12 @@ func (s *Store) slowSearchIpv6(ipAddr string) string {
if isReservedIP(ipAddr) { if isReservedIP(ipAddr) {
return "" return ""
} }
//Check if already in cache
if cc, ok := s.slowLookupCacheIpv6[ipAddr]; ok {
return cc
}
for _, ipRange := range s.geodbIpv6 { for _, ipRange := range s.geodbIpv6 {
startIp := ipRange[0] startIp := ipRange[0]
endIp := ipRange[1] endIp := ipRange[1]
@ -80,6 +94,8 @@ func (s *Store) slowSearchIpv6(ipAddr string) string {
inRange, _ := isIPv6InRange(startIp, endIp, ipAddr) inRange, _ := isIPv6InRange(startIp, endIp, ipAddr)
if inRange { if inRange {
//Add to cache
s.slowLookupCacheIpv6[ipAddr] = cc
return cc return cc
} }
} }

View File

@ -27,18 +27,18 @@ func ReverseProxtInit() {
/* /*
Load Reverse Proxy Global Settings Load Reverse Proxy Global Settings
*/ */
inboundPort := 80 inboundPort := 443
if sysdb.KeyExists("settings", "inbound") { if sysdb.KeyExists("settings", "inbound") {
sysdb.Read("settings", "inbound", &inboundPort) sysdb.Read("settings", "inbound", &inboundPort)
SystemWideLogger.Println("Serving inbound port ", inboundPort) SystemWideLogger.Println("Serving inbound port ", inboundPort)
} else { } else {
SystemWideLogger.Println("Inbound port not set. Using default (80)") SystemWideLogger.Println("Inbound port not set. Using default (443)")
} }
useTls := false useTls := true
sysdb.Read("settings", "usetls", &useTls) sysdb.Read("settings", "usetls", &useTls)
if useTls { if useTls {
SystemWideLogger.Println("TLS mode enabled. Serving proxxy request with TLS") SystemWideLogger.Println("TLS mode enabled. Serving proxy request with TLS")
} else { } else {
SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http") SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http")
} }
@ -59,7 +59,7 @@ func ReverseProxtInit() {
SystemWideLogger.Println("Development mode disabled. Proxying with default Cache Control policy") SystemWideLogger.Println("Development mode disabled. Proxying with default Cache Control policy")
} }
listenOnPort80 := false listenOnPort80 := true
sysdb.Read("settings", "listenP80", &listenOnPort80) sysdb.Read("settings", "listenP80", &listenOnPort80)
if listenOnPort80 { if listenOnPort80 {
SystemWideLogger.Println("Port 80 listener enabled") SystemWideLogger.Println("Port 80 listener enabled")
@ -67,7 +67,7 @@ func ReverseProxtInit() {
SystemWideLogger.Println("Port 80 listener disabled") SystemWideLogger.Println("Port 80 listener disabled")
} }
forceHttpsRedirect := false forceHttpsRedirect := true
sysdb.Read("settings", "redirect", &forceHttpsRedirect) sysdb.Read("settings", "redirect", &forceHttpsRedirect)
if forceHttpsRedirect { if forceHttpsRedirect {
SystemWideLogger.Println("Force HTTPS mode enabled") SystemWideLogger.Println("Force HTTPS mode enabled")
@ -1085,6 +1085,7 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
if dynamicProxyRouter.Running { if dynamicProxyRouter.Running {
dynamicProxyRouter.StopProxyService() dynamicProxyRouter.StopProxyService()
dynamicProxyRouter.Option.Port = newIncomingPortInt dynamicProxyRouter.Option.Port = newIncomingPortInt
time.Sleep(1 * time.Second) //Fixed start fail issue
dynamicProxyRouter.StartProxyService() dynamicProxyRouter.StartProxyService()
} else { } else {
//Only change setting but not starting the proxy service //Only change setting but not starting the proxy service

View File

@ -103,8 +103,9 @@ func startupSequence() {
//Create a geodb store //Create a geodb store
geodbStore, err = geodb.NewGeoDb(sysdb, &geodb.StoreOptions{ geodbStore, err = geodb.NewGeoDb(sysdb, &geodb.StoreOptions{
AllowSlowIpv4LookUp: !*enableHighSpeedGeoIPLookup, AllowSlowIpv4LookUp: !*enableHighSpeedGeoIPLookup,
AllowSloeIpv6Lookup: !*enableHighSpeedGeoIPLookup, AllowSlowIpv6Lookup: !*enableHighSpeedGeoIPLookup,
SlowLookupCacheClearInterval: GEODB_CACHE_CLEAR_INTERVAL * time.Minute,
}) })
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -295,15 +295,25 @@
//Automatic check if the site require TLS and check the checkbox if needed //Automatic check if the site require TLS and check the checkbox if needed
function autoCheckTls(targetDomain){ function autoCheckTls(targetDomain){
$.cjax({ $.cjax({
url: "/api/proxy/tlscheck", url: "/api/proxy/tlscheck?selfsignchk=true",
data: {url: targetDomain}, data: {url: targetDomain},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
msgbox(data.error, false); msgbox(data.error, false);
}else if (data == "https"){ }else{
$("#reqTls").parent().checkbox("set checked"); //Check if the site require TLS
}else if (data == "http"){ if (data.protocol == "https"){
$("#reqTls").parent().checkbox("set unchecked"); $("#reqTls").parent().checkbox("set checked");
}else if (data.protocol == "http"){
$("#reqTls").parent().checkbox("set unchecked");
}
//Check if the site is using self-signed cert
if (data.selfsign){
$("#skipTLSValidation").parent().checkbox("set checked");
}else{
$("#skipTLSValidation").parent().checkbox("set unchecked");
}
} }
} }
}) })

View File

@ -73,25 +73,27 @@
<p>Inbound Port (Reverse Proxy Listening Port)</p> <p>Inbound Port (Reverse Proxy Listening Port)</p>
<div class="ui action fluid notloopbackOnly input" tourstep="incomingPort"> <div class="ui action fluid notloopbackOnly input" tourstep="incomingPort">
<small id="applyButtonReminder">Click "Apply" button to confirm listening port changes</small> <small id="applyButtonReminder">Click "Apply" button to confirm listening port changes</small>
<input type="text" id="incomingPort" placeholder="Incoming Port" value="80"> <input type="text" id="incomingPort" placeholder="Incoming Port" value="443">
<button class="ui green notloopbackOnly button" style="background: linear-gradient(60deg, #27e7ff, #00ca52);" onclick="handlePortChange();"><i class="ui checkmark icon"></i> Apply</button> <button class="ui green notloopbackOnly button" style="background: linear-gradient(60deg, #27e7ff, #00ca52);" onclick="handlePortChange();"><i class="ui checkmark icon"></i> Apply</button>
</div> </div>
<br> <br>
<div id="tls" class="ui toggle notloopbackOnly checkbox"> <div id="tls" class="ui toggle notloopbackOnly checkbox">
<input type="checkbox"> <input type="checkbox">
<label>Use TLS to serve proxy request</label> <label>Use TLS to serve proxy request<br>
<small>Also known as HTTPS mode</small></label>
</div> </div>
<br> <br>
<div id="listenP80" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em;" > <div id="listenP80" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.4em;" >
<input type="checkbox"> <input type="checkbox">
<label>Enable HTTP server on port 80<br> <label>Enable HTTP server on port 80<br>
<small>(Only apply when TLS enabled and not using port 80)</small></label> <small>Accept HTTP requests even if you are using HTTPS mode</small></label>
</div> </div>
<br> <br>
<div tourstep="forceHttpsRedirect" style="display: inline-block;"> <div tourstep="forceHttpsRedirect" style="display: inline-block;">
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em; padding-left: 2em;"> <div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.4em;">
<input type="checkbox"> <input type="checkbox">
<label>Force redirect HTTP request to HTTPS</label> <label>Force redirect HTTP request to HTTPS<br>
<small>Redirect web traffic from port 80 to 443, require enabling HTTP server on port 80</small></label>
</div> </div>
</div> </div>
<div class="ui basic segment advanceoptions"> <div class="ui basic segment advanceoptions">
@ -359,10 +361,10 @@
return; return;
} }
if (enabled){ if (enabled){
$("#redirect").show(); //$("#redirect").show();
msgbox("Port 80 listener enabled"); msgbox("Port 80 listener enabled");
}else{ }else{
$("#redirect").hide(); //$("#redirect").hide();
msgbox("Port 80 listener disabled"); msgbox("Port 80 listener disabled");
} }
} }
@ -400,10 +402,10 @@
$.get("/api/proxy/listenPort80", function(data){ $.get("/api/proxy/listenPort80", function(data){
if (data){ if (data){
$("#listenP80").checkbox("set checked"); $("#listenP80").checkbox("set checked");
$("#redirect").show(); //$("#redirect").show();
}else{ }else{
$("#listenP80").checkbox("set unchecked"); $("#listenP80").checkbox("set unchecked");
$("#redirect").hide(); //$("#redirect").hide();
} }
$("#listenP80").find("input").on("change", function(){ $("#listenP80").find("input").on("change", function(){

View File

@ -191,14 +191,19 @@
var targetDomain = $("#virtualDirectoryDomain").val().trim(); var targetDomain = $("#virtualDirectoryDomain").val().trim();
if (targetDomain != ""){ if (targetDomain != ""){
$.cjax({ $.cjax({
url: "/api/proxy/tlscheck", url: "/api/proxy/tlscheck?selfsignchk=true",
data: {url: targetDomain}, data: {url: targetDomain},
success: function(data){ success: function(data){
if (data.error != undefined){ if (data.error != undefined){
}else if (data == "https"){ }else if (data.protocol == "https"){
$("#vdReqTls").parent().checkbox("set checked"); $("#vdReqTls").parent().checkbox("set checked");
}else if (data == "http"){ if (data.selfsign){
$("#vdSkipTLSValidation").parent().checkbox("set checked");
}else{
$("#vdSkipTLSValidation").parent().checkbox("set unchecked");
}
}else if (data.protocol == "http"){
$("#vdReqTls").parent().checkbox("set unchecked"); $("#vdReqTls").parent().checkbox("set unchecked");
} }
} }

View File

@ -18,11 +18,9 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"time"
"imuslab.com/zoraxy/mod/dynamicproxy" "imuslab.com/zoraxy/mod/dynamicproxy"
"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance" "imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
@ -32,39 +30,6 @@ import (
"imuslab.com/zoraxy/mod/wakeonlan" "imuslab.com/zoraxy/mod/wakeonlan"
) )
/*
Proxy Utils
*/
//Check if site support TLS
func HandleCheckSiteSupportTLS(w http.ResponseWriter, r *http.Request) {
targetURL, err := utils.PostPara(r, "url")
if err != nil {
utils.SendErrorResponse(w, "invalid url given")
return
}
httpsUrl := fmt.Sprintf("https://%s", targetURL)
httpUrl := fmt.Sprintf("http://%s", targetURL)
client := http.Client{Timeout: 5 * time.Second}
resp, err := client.Head(httpsUrl)
if err == nil && resp.StatusCode == http.StatusOK {
js, _ := json.Marshal("https")
utils.SendJSONResponse(w, string(js))
return
}
resp, err = client.Head(httpUrl)
if err == nil && resp.StatusCode == http.StatusOK {
js, _ := json.Marshal("http")
utils.SendJSONResponse(w, string(js))
return
}
utils.SendErrorResponse(w, "invalid url given")
}
/* /*
Statistic Summary Statistic Summary
*/ */