Skip to content

Commit

Permalink
Merge pull request #449 from tobychui/v3.1.6
Browse files Browse the repository at this point in the history
- Exposed log file, sys.uuid and static web server path to start flag
- Optimized connection close implementation
- Added toggle for uptime monitor
- Added optional copy HTTP custom headers to websocket connection
  • Loading branch information
tobychui authored Dec 31, 2024
2 parents 1a6a87e + be5797c commit d1e5581
Show file tree
Hide file tree
Showing 27 changed files with 3,363 additions and 2,390 deletions.
1 change: 1 addition & 0 deletions src/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func RegisterHTTPProxyAPIs(authRouter *auth.RouterDef) {
authRouter.HandleFunc("/api/proxy/header/handleHopByHop", HandleHopByHop)
authRouter.HandleFunc("/api/proxy/header/handleHostOverwrite", HandleHostOverwrite)
authRouter.HandleFunc("/api/proxy/header/handlePermissionPolicy", HandlePermissionPolicy)
authRouter.HandleFunc("/api/proxy/header/handleWsHeaderBehavior", HandleWsHeaderBehavior)
/* Reverse proxy auth related */
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)
Expand Down
50 changes: 24 additions & 26 deletions src/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func LoadReverseProxyConfig(configFilepath string) error {
}

//Parse it into dynamic proxy endpoint
thisConfigEndpoint := dynamicproxy.ProxyEndpoint{}
thisConfigEndpoint := dynamicproxy.GetDefaultProxyEndpoint()
err = json.Unmarshal(endpointConfig, &thisConfigEndpoint)
if err != nil {
return err
Expand Down Expand Up @@ -129,31 +129,23 @@ func RemoveReverseProxyConfig(endpoint string) error {
// Get the default root config that point to the internal static web server
// this will be used if root config is not found (new deployment / missing root.config file)
func GetDefaultRootConfig() (*dynamicproxy.ProxyEndpoint, error) {
//Default Authentication Provider
defaultAuth := &dynamicproxy.AuthenticationProvider{
AuthMethod: dynamicproxy.AuthMethodNone,
BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{},
BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
//Get the default proxy endpoint
rootProxyEndpointConfig := dynamicproxy.GetDefaultProxyEndpoint()
rootProxyEndpointConfig.ProxyType = dynamicproxy.ProxyTypeRoot
rootProxyEndpointConfig.RootOrMatchingDomain = "/"
rootProxyEndpointConfig.ActiveOrigins = []*loadbalance.Upstream{
{
OriginIpOrDomain: "127.0.0.1:" + staticWebServer.GetListeningPort(),
RequireTLS: false,
SkipCertValidations: false,
Weight: 0,
},
}
rootProxyEndpointConfig.DefaultSiteOption = dynamicproxy.DefaultSite_InternalStaticWebServer
rootProxyEndpointConfig.DefaultSiteValue = ""

//Default settings
rootProxyEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&dynamicproxy.ProxyEndpoint{
ProxyType: dynamicproxy.ProxyTypeRoot,
RootOrMatchingDomain: "/",
ActiveOrigins: []*loadbalance.Upstream{
{
OriginIpOrDomain: "127.0.0.1:" + staticWebServer.GetListeningPort(),
RequireTLS: false,
SkipCertValidations: false,
Weight: 0,
},
},
InactiveOrigins: []*loadbalance.Upstream{},
BypassGlobalTLS: false,
VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
AuthenticationProvider: defaultAuth,
DefaultSiteOption: dynamicproxy.DefaultSite_InternalStaticWebServer,
DefaultSiteValue: "",
})
rootProxyEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&rootProxyEndpointConfig)
if err != nil {
return nil, err
}
Expand All @@ -175,7 +167,11 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) {
}

// Specify the folder path to be zipped
folderPath := "./conf/"
if !utils.FileExists("./conf") {
SystemWideLogger.PrintAndLog("Backup", "Configuration folder not found", nil)
return
}
folderPath := "./conf"

// Set the Content-Type header to indicate it's a zip file
w.Header().Set("Content-Type", "application/zip")
Expand Down Expand Up @@ -230,7 +226,7 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) {
}

// Open the file on disk
file, err := os.Open("sys.db")
file, err := os.Open("./sys.db")
if err != nil {
SystemWideLogger.PrintAndLog("Backup", "Unable to open sysdb", err)
return
Expand Down Expand Up @@ -279,6 +275,8 @@ func ImportConfigFromZip(w http.ResponseWriter, r *http.Request) {
targetDir := "./conf"
if utils.FileExists(targetDir) {
//Backup the old config to old
//backupPath := filepath.Dir(*path_conf) + filepath.Base(*path_conf) + ".old_" + strconv.Itoa(int(time.Now().Unix()))
//os.Rename(*path_conf, backupPath)
os.Rename("./conf", "./conf.old_"+strconv.Itoa(int(time.Now().Unix())))
}

Expand Down
12 changes: 8 additions & 4 deletions src/def.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,10 @@ import (
const (
/* Build Constants */
SYSTEM_NAME = "Zoraxy"
SYSTEM_VERSION = "3.1.5"
SYSTEM_VERSION = "3.1.6"
DEVELOPMENT_BUILD = false /* Development: Set to false to use embedded web fs */

/* System Constants */
DATABASE_PATH = "sys.db"
TMP_FOLDER = "./tmp"
WEBSERV_DEFAULT_PORT = 5487
MDNS_HOSTNAME_PREFIX = "zoraxy_" /* Follow by node UUID */
Expand All @@ -59,7 +58,6 @@ const (
ACME_AUTORENEW_CONFIG_PATH = "./conf/acme_conf.json"
CSRF_COOKIENAME = "zoraxy_csrf"
LOG_PREFIX = "zr"
LOG_FOLDER = "./log"
LOG_EXTENSION = ".log"

/* Configuration Folder Storage Path Constants */
Expand All @@ -86,10 +84,16 @@ var (
acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
acmeCertAutoRenewDays = flag.Int("earlyrenew", 30, "Number of days to early renew a soon expiring certificate (days)")
enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters")
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")

/* Path Configuration Flags */
//path_database = flag.String("dbpath", "./sys.db", "Database path")
//path_conf = flag.String("conf", "./conf", "Configuration folder path")
path_uuid = flag.String("uuid", "./sys.uuid", "sys.uuid file path")
path_logFile = flag.String("log", "./log", "Log folder path")
path_webserver = flag.String("webroot", "./www", "Static web server root folder. Only allow change in start paramters")

/* Maintaince Function Flags */
geoDbUpdate = flag.Bool("update_geoip", false, "Download the latest GeoIP data and exit")
)
Expand Down
2 changes: 1 addition & 1 deletion src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func main() {
SetupCloseHandler()

//Read or create the system uuid
uuidRecord := "./sys.uuid"
uuidRecord := *path_uuid
if !utils.FileExists(uuidRecord) {
newSystemUUID := uuid.New().String()
os.WriteFile(uuidRecord, []byte(newSystemUUID), 0775)
Expand Down
2 changes: 2 additions & 0 deletions src/mod/dynamicproxy/Server.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,12 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
h.Parent.logRequest(r, false, 444, "root-noresponse", domainOnly)
hijacker, ok := w.(http.Hijacker)
if !ok {
w.Header().Set("Connection", "close")
return
}
conn, _, err := hijacker.Hijack()
if err != nil {
w.Header().Set("Connection", "close")
return
}
conn.Close()
Expand Down
65 changes: 65 additions & 0 deletions src/mod/dynamicproxy/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package dynamicproxy

import (
"github.com/google/uuid"
"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
"imuslab.com/zoraxy/mod/dynamicproxy/rewrite"
)

/*
Default Provider
This script provide the default options for all datatype
provided by dynamicproxy module
*/

// GetDefaultAuthenticationProvider return a default authentication provider
func GetDefaultAuthenticationProvider() *AuthenticationProvider {
return &AuthenticationProvider{
AuthMethod: AuthMethodNone,
BasicAuthCredentials: []*BasicAuthCredentials{},
BasicAuthExceptionRules: []*BasicAuthExceptionRule{},
BasicAuthGroupIDs: []string{},
AutheliaURL: "",
UseHTTPS: false,
}
}

// GetDefaultHeaderRewriteRules return a default header rewrite rules
func GetDefaultHeaderRewriteRules() *HeaderRewriteRules {
return &HeaderRewriteRules{
UserDefinedHeaders: []*rewrite.UserDefinedHeader{},
RequestHostOverwrite: "",
HSTSMaxAge: 0,
EnablePermissionPolicyHeader: false,
PermissionPolicy: nil,
DisableHopByHopHeaderRemoval: false,
}
}

// GetDefaultProxyEndpoint return a default proxy endpoint
func GetDefaultProxyEndpoint() ProxyEndpoint {
randomPrefix := uuid.New().String()
return ProxyEndpoint{
ProxyType: ProxyTypeHost,
RootOrMatchingDomain: randomPrefix + ".internal",
MatchingDomainAlias: []string{},
ActiveOrigins: []*loadbalance.Upstream{},
InactiveOrigins: []*loadbalance.Upstream{},
UseStickySession: false,
UseActiveLoadBalance: false,
Disabled: false,
BypassGlobalTLS: false,
VirtualDirectories: []*VirtualDirectoryEndpoint{},
HeaderRewriteRules: GetDefaultHeaderRewriteRules(),
EnableWebsocketCustomHeaders: false,
AuthenticationProvider: GetDefaultAuthenticationProvider(),
RequireRateLimit: false,
RateLimit: 0,
DisableUptimeMonitor: false,
AccessFilterUUID: "default",
DefaultSiteOption: DefaultSite_InternalStaticWebServer,
DefaultSiteValue: "",
}
}
17 changes: 17 additions & 0 deletions src/mod/dynamicproxy/domainsniff/domainsniff.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ func DomainUsesTLS(targetURL string) bool {
return false
}

/*
WebSocket Header Sniff
*/

// Check if the requst is a special case where
// user defined header shall not be passed over

func RequireWebsocketHeaderCopy(r *http.Request) bool {
//Return false for proxmox
if IsProxmox(r) {
return false
}

//Add more edge cases here
return true
}

/*
Request Handlers
*/
Expand Down
10 changes: 8 additions & 2 deletions src/mod/dynamicproxy/dynamicproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,18 @@ func (router *Router) StartProxyService() error {
router.Option.Logger.PrintAndLog("dprouter", "failed to get upstream for hostname", err)
router.logRequest(r, false, 404, "vdir-http", r.Host)
}

endpointProxyRewriteRules := GetDefaultHeaderRewriteRules()
if sep.HeaderRewriteRules != nil {
endpointProxyRewriteRules = sep.HeaderRewriteRules
}

selectedUpstream.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
ProxyDomain: selectedUpstream.OriginIpOrDomain,
OriginalHost: originalHostHeader,
UseTLS: selectedUpstream.RequireTLS,
HostHeaderOverwrite: sep.HeaderRewriteRules.RequestHostOverwrite,
NoRemoveHopByHop: sep.HeaderRewriteRules.DisableHopByHopHeaderRemoval,
HostHeaderOverwrite: endpointProxyRewriteRules.RequestHostOverwrite,
NoRemoveHopByHop: endpointProxyRewriteRules.DisableHopByHopHeaderRemoval,
PathPrefix: "",
Version: sep.parent.Option.HostVersion,
})
Expand Down
15 changes: 13 additions & 2 deletions src/mod/dynamicproxy/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ import (

// Check if a user define header exists in this endpoint, ignore case
func (ep *ProxyEndpoint) UserDefinedHeaderExists(key string) bool {
for _, header := range ep.HeaderRewriteRules.UserDefinedHeaders {
endpointProxyRewriteRules := GetDefaultHeaderRewriteRules()
if ep.HeaderRewriteRules != nil {
endpointProxyRewriteRules = ep.HeaderRewriteRules
}

for _, header := range endpointProxyRewriteRules.UserDefinedHeaders {
if strings.EqualFold(header.Key, key) {
return true
}
Expand All @@ -38,6 +43,9 @@ func (ep *ProxyEndpoint) UserDefinedHeaderExists(key string) bool {
// Remvoe a user defined header from the list
func (ep *ProxyEndpoint) RemoveUserDefinedHeader(key string) error {
newHeaderList := []*rewrite.UserDefinedHeader{}
if ep.HeaderRewriteRules == nil {
ep.HeaderRewriteRules = GetDefaultHeaderRewriteRules()
}
for _, header := range ep.HeaderRewriteRules.UserDefinedHeaders {
if !strings.EqualFold(header.Key, key) {
newHeaderList = append(newHeaderList, header)
Expand All @@ -55,6 +63,9 @@ func (ep *ProxyEndpoint) AddUserDefinedHeader(newHeaderRule *rewrite.UserDefined
ep.RemoveUserDefinedHeader(newHeaderRule.Key)
}

if ep.HeaderRewriteRules == nil {
ep.HeaderRewriteRules = GetDefaultHeaderRewriteRules()
}
newHeaderRule.Key = cases.Title(language.Und, cases.NoLower).String(newHeaderRule.Key)
ep.HeaderRewriteRules.UserDefinedHeaders = append(ep.HeaderRewriteRules.UserDefinedHeaders, newHeaderRule)
return nil
Expand Down Expand Up @@ -106,7 +117,7 @@ func (ep *ProxyEndpoint) RemoveVirtualDirectoryRuleByMatchingPath(matchingPath s
return errors.New("target virtual directory routing rule not found")
}

// Delete a vdir rule by its matching path
// Add a vdir rule by its matching path
func (ep *ProxyEndpoint) AddVirtualDirectoryRule(vdir *VirtualDirectoryEndpoint) (*ProxyEndpoint, error) {
//Check for matching path duplicate
if ep.GetVirtualDirectoryRuleByMatchingPath(vdir.MatchingPath) != nil {
Expand Down
Loading

0 comments on commit d1e5581

Please sign in to comment.